Skip to content

Commit

Permalink
[python] Add ICD support to Python binding
Browse files Browse the repository at this point in the history
  • Loading branch information
erjiaqing committed May 23, 2024
1 parent 263e583 commit 4b9334f
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 5 deletions.
6 changes: 5 additions & 1 deletion src/controller/python/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ shared_library("ChipDeviceCtrl") {
"chip/crypto/p256keypair.cpp",
"chip/crypto/p256keypair.h",
"chip/discovery/NodeResolution.cpp",
"chip/icd/CheckInDelegate.cpp",
"chip/icd/CheckInDelegate.h",
"chip/interaction_model/Delegate.cpp",
"chip/interaction_model/Delegate.h",
"chip/internal/ChipThreadWork.cpp",
Expand Down Expand Up @@ -121,7 +123,7 @@ shared_library("ChipDeviceCtrl") {

public_deps = [
"${chip_root}/src/app",
"${chip_root}/src/app/icd/client:manager",
"${chip_root}/src/app/icd/client:handler",
"${chip_root}/src/app/server",
"${chip_root}/src/credentials:default_attestation_verifier",
"${chip_root}/src/lib",
Expand Down Expand Up @@ -251,6 +253,7 @@ chip_python_wheel_action("chip-core") {
"chip/discovery/library_handle.py",
"chip/discovery/types.py",
"chip/exceptions/__init__.py",
"chip/icd/__init__.py",
"chip/interaction_model/__init__.py",
"chip/interaction_model/delegate.py",
"chip/internal/__init__.py",
Expand Down Expand Up @@ -306,6 +309,7 @@ chip_python_wheel_action("chip-core") {
"chip.credentials",
"chip.crypto",
"chip.utils",
"chip.icd",
"chip.discovery",
"chip.exceptions",
"chip.internal",
Expand Down
59 changes: 56 additions & 3 deletions src/controller/python/ChipDeviceController-ScriptBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@

#include <app/DeviceProxy.h>
#include <app/InteractionModelEngine.h>
#include <app/server/Dnssd.h>
#include <app/icd/client/CheckInHandler.h>
#include <app/icd/client/DefaultCheckInDelegate.h>
#include <app/icd/client/DefaultICDClientStorage.h>
#include <app/server/Dnssd.h>
#include <controller/AutoCommissioner.h>
#include <controller/CHIPDeviceController.h>
#include <controller/CHIPDeviceControllerFactory.h>
Expand All @@ -57,6 +58,7 @@
#include <controller/python/ChipDeviceController-ScriptDevicePairingDelegate.h>
#include <controller/python/ChipDeviceController-ScriptPairingDeviceDiscoveryDelegate.h>
#include <controller/python/ChipDeviceController-StorageDelegate.h>
#include <controller/python/chip/icd/CheckInDelegate.h>
#include <controller/python/chip/interaction_model/Delegate.h>
#include <controller/python/chip/native/PyChipError.h>

Expand Down Expand Up @@ -103,21 +105,23 @@ chip::Platform::ScopedMemoryBuffer<char> sDefaultNTPBuf;
app::Clusters::TimeSynchronization::Structs::DSTOffsetStruct::Type sDSTBuf;
app::Clusters::TimeSynchronization::Structs::TimeZoneStruct::Type sTimeZoneBuf;
chip::Platform::ScopedMemoryBuffer<char> sTimeZoneNameBuf;
chip::Controller::CommissioningParameters sCommissioningParameters;

} // namespace

chip::Controller::CommissioningParameters sCommissioningParameters;
chip::app::DefaultICDClientStorage sICDClientStorage;
chip::Controller::ScriptPairingDeviceDiscoveryDelegate sPairingDeviceDiscoveryDelegate;
chip::Credentials::GroupDataProviderImpl sGroupDataProvider;
chip::Credentials::PersistentStorageOpCertStore sPersistentStorageOpCertStore;
chip::Crypto::RawKeySessionKeystore sSessionKeystore;

chip::app::CheckInHandler sCheckInHandler;

// NOTE: Remote device ID is in sync with the echo server device id
// At some point, we may want to add an option to connect to a device without
// knowing its id, because the ID can be learned on the first response that is received.
chip::NodeId kDefaultLocalDeviceId = chip::kTestControllerNodeId;
chip::NodeId kRemoteDeviceId = chip::kTestDeviceNodeId;
uint8_t sICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length];

extern "C" {
PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter * storageAdapter, bool enableServerInteractions);
Expand Down Expand Up @@ -147,6 +151,9 @@ PyChipError pychip_DeviceController_SetDSTOffset(int32_t offset, uint64_t validS
PyChipError pychip_DeviceController_SetDefaultNtp(const char * defaultNTP);
PyChipError pychip_DeviceController_SetTrustedTimeSource(chip::NodeId nodeId, chip::EndpointId endpoint);
PyChipError pychip_DeviceController_SetCheckMatchingFabric(bool check);
PyChipError pychip_DeviceController_SetIcdRegistrationParameters(chip::Controller::DeviceCommissioner * devCtrl, bool enabled,
uint8_t * icdSymmetricKeyOrNull, uint64_t icdCheckInNodeIdOrZero,
uint64_t icdMonitoredSubjectOrZero, uint32_t icdStayActiveMsec);
PyChipError pychip_DeviceController_ResetCommissioningParameters();
PyChipError pychip_DeviceController_CloseSession(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid);
PyChipError pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr,
Expand Down Expand Up @@ -266,6 +273,12 @@ PyChipError pychip_DeviceController_StackInit(Controller::Python::StorageAdapter

sICDClientStorage.Init(storageAdapter, &sSessionKeystore);

auto engine = chip::app::InteractionModelEngine::GetInstance();
PyReturnErrorOnFailure(ToPyChipError(PyChipCheckInDelegate::GetInstance().Init(&sICDClientStorage, engine)));
PyReturnErrorOnFailure(
ToPyChipError(sCheckInHandler.Init(DeviceControllerFactory::GetInstance().GetSystemState()->ExchangeMgr(),
&sICDClientStorage, &PyChipCheckInDelegate::GetInstance(), engine)));

sGroupDataProvider.SetStorageDelegate(storageAdapter);
sGroupDataProvider.SetSessionKeystore(factoryParams.sessionKeystore);
PyReturnErrorOnFailure(ToPyChipError(sGroupDataProvider.Init()));
Expand Down Expand Up @@ -560,6 +573,46 @@ PyChipError pychip_DeviceController_SetCheckMatchingFabric(bool check)
return ToPyChipError(CHIP_NO_ERROR);
}

PyChipError pychip_DeviceController_SetIcdRegistrationParameters(chip::Controller::DeviceCommissioner * devCtrl, bool enabled,
uint8_t * icdSymmetricKeyOrNull, uint64_t icdCheckInNodeIdOrZero,
uint64_t icdMonitoredSubjectOrZero, uint32_t icdStayActiveMsec)
{
if (!enabled)
{
return ToPyChipError(CHIP_NO_ERROR);
}

sCommissioningParameters.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);

if (icdSymmetricKeyOrNull != nullptr)
{
memcpy(sICDSymmetricKey, icdSymmetricKeyOrNull, sizeof(sICDSymmetricKey));
}
else
{
chip::Crypto::DRBG_get_bytes(sICDSymmetricKey, sizeof(sICDSymmetricKey));
}
if (icdCheckInNodeIdOrZero == 0)
{
icdCheckInNodeIdOrZero = devCtrl->GetNodeId();
}
if (icdMonitoredSubjectOrZero == 0)
{
icdMonitoredSubjectOrZero = icdCheckInNodeIdOrZero;
}
// These Optionals must have values now.
// The commissioner will verify these values.
sCommissioningParameters.SetICDSymmetricKey(ByteSpan(sICDSymmetricKey));
if (icdStayActiveMsec != 0)
{
sCommissioningParameters.SetICDStayActiveDurationMsec(icdStayActiveMsec);
}
sCommissioningParameters.SetICDCheckInNodeId(icdCheckInNodeIdOrZero);
sCommissioningParameters.SetICDMonitoredSubject(icdMonitoredSubjectOrZero);

return ToPyChipError(CHIP_NO_ERROR);
}

PyChipError pychip_DeviceController_ResetCommissioningParameters()
{
sCommissioningParameters = CommissioningParameters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@

#include "ChipDeviceController-ScriptDevicePairingDelegate.h"
#include "lib/support/TypeTraits.h"
#include <app/icd/client/DefaultICDClientStorage.h>
#include <controller/python/chip/native/PyChipError.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>

#include <string>

extern chip::app::DefaultICDClientStorage sICDClientStorage;
extern chip::Controller::CommissioningParameters sCommissioningParameters;
extern uint8_t sICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length];

namespace chip {
namespace Controller {

Expand Down Expand Up @@ -180,5 +185,40 @@ ScriptDevicePairingDelegate::GetOpenWindowCallback(Controller::CommissioningWind
return &mOpenWindowCallback;
}

void ScriptDevicePairingDelegate::OnICDRegistrationComplete(NodeId nodeId, uint32_t icdCounter)
{
app::ICDClientInfo clientInfo;
clientInfo.peer_node = ScopedNodeId(nodeId, mFabricIndex);
clientInfo.monitored_subject = sCommissioningParameters.GetICDMonitoredSubject().Value();
clientInfo.start_icd_counter = icdCounter;

CHIP_ERROR err = sICDClientStorage.SetKey(clientInfo, ByteSpan(sICDSymmetricKey));
if (err == CHIP_NO_ERROR)
{
err = sICDClientStorage.StoreEntry(clientInfo);
}

if (err != CHIP_NO_ERROR)
{
sICDClientStorage.RemoveKey(clientInfo);
ChipLogError(chipTool, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(nodeId),
err.AsString());
return;
}

ChipLogProgress(Zcl, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(nodeId));
ChipLogProgress(Zcl,
"ICD Registration Complete for device " ChipLogFormatX64 " / Check-In NodeID: " ChipLogFormatX64
" / Monitored Subject: " ChipLogFormatX64 " / ICDCounter %u",
ChipLogValueX64(nodeId), ChipLogValueX64(sCommissioningParameters.GetICDCheckInNodeId().Value()),
ChipLogValueX64(clientInfo.monitored_subject), icdCounter);
}

void ScriptDevicePairingDelegate::OnICDStayActiveComplete(NodeId deviceId, uint32_t promisedActiveDuration)
{
ChipLogProgress(Zcl, "ICD Stay Active Complete for device " ChipLogFormatX64 " / promisedActiveDuration: %u",
ChipLogValueX64(deviceId), promisedActiveDuration);
}

} // namespace Controller
} // namespace chip
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,14 @@ class ScriptDevicePairingDelegate final : public Controller::DevicePairingDelega
void OnCommissioningFailure(PeerId peerId, CHIP_ERROR error, CommissioningStage stageFailed,
Optional<Credentials::AttestationVerificationResult> additionalErrorInfo) override;
void OnCommissioningStatusUpdate(PeerId peerId, CommissioningStage stageCompleted, CHIP_ERROR error) override;
void OnICDRegistrationComplete(NodeId deviceId, uint32_t icdCounter) override;
void OnICDStayActiveComplete(NodeId deviceId, uint32_t promisedActiveDuration) override;
void OnFabricCheck(NodeId matchingNodeId) override;
Callback::Callback<Controller::OnOpenCommissioningWindow> *
GetOpenWindowCallback(Controller::CommissioningWindowOpener * context);
void OnOpenCommissioningWindow(NodeId deviceId, CHIP_ERROR status, SetupPayload payload);
void SetExpectingPairingComplete(bool value) { expectingPairingComplete = value; }
void SetFabricIndex(FabricIndex fabricIndex) { mFabricIndex = fabricIndex; }

private:
DevicePairingDelegate_OnPairingCompleteFunct mOnPairingCompleteCallback = nullptr;
Expand All @@ -85,6 +88,7 @@ class ScriptDevicePairingDelegate final : public Controller::DevicePairingDelega
Callback::Callback<Controller::OnOpenCommissioningWindow> mOpenWindowCallback;
Controller::CommissioningWindowOpener * mWindowOpener = nullptr;
bool expectingPairingComplete = false;
FabricIndex mFabricIndex;
};

} // namespace Controller
Expand Down
1 change: 1 addition & 0 deletions src/controller/python/OpCredsBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

sICDClientStorage.UpdateFabricList(devCtrl->GetFabricIndex());
pairingDelegate->SetFabricIndex(devCtrl->GetFabricIndex());

*outDevCtrl = devCtrl.release();
*outPairingDelegate = pairingDelegate.release();
Expand Down
26 changes: 26 additions & 0 deletions src/controller/python/chip/ChipDeviceCtrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,11 @@ def _InitLib(self):
self._dmLib.pychip_DeviceController_SetCheckMatchingFabric.restype = PyChipError
self._dmLib.pychip_DeviceController_SetCheckMatchingFabric.argtypes = [c_bool]

self._dmLib.pychip_DeviceController_SetIcdRegistrationParameters.restype = PyChipError
self._dmLib.pychip_DeviceController_SetIcdRegistrationParameters.argtypes = [
c_void_p, c_bool, c_char_p, c_uint64, c_uint64, c_uint32
]

self._dmLib.pychip_DeviceController_ResetCommissioningParameters.restype = PyChipError
self._dmLib.pychip_DeviceController_ResetCommissioningParameters.argtypes = []

Expand Down Expand Up @@ -1979,6 +1984,27 @@ def SetCheckMatchingFabric(self, check: bool):
lambda: self._dmLib.pychip_DeviceController_SetCheckMatchingFabric(check)
).raise_on_error()

def SetIcdRegistrationParameters(self, symmetricKey: bytes = None, checkInNodeId: int = 0, monitoredSubject: int = 0, stayActiveMs: int = 0):
if symmetricKey is not None:
if len(symmetricKey) != 16:
raise ValueError("symmetricKey should be 16 bytes")
if not checkInNodeId:
checkInNodeId = self._nodeId
if not monitoredSubject:
monitoredSubject = checkInNodeId

self.CheckIsActive()
self._ChipStack.Call(
lambda: self._dmLib.pychip_DeviceController_SetIcdRegistrationParameters(
self.devCtrl, True, symmetricKey, checkInNodeId, monitoredSubject, stayActiveMs)
).raise_on_error()

def DisableIcdRegistration(self):
self.CheckIsActive()
self._ChipStack.Call(
lambda: self._dmLib.pychip_DeviceController_SetIcdRegistrationParameters(self.devCtrl, False, None, 0, 0, 0)
).raise_on_error()

def GetFabricCheckResult(self) -> int:
''' Returns the fabric check result if SetCheckMatchingFabric was used.'''
return self.fabricCheckNodeId
Expand Down
4 changes: 3 additions & 1 deletion src/controller/python/chip/ChipReplStartup.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ def main():
"-d", "--debug", help="Set default logging level to debug.", action="store_true")
parser.add_argument(
"-t", "--trust-store", help="Path to the PAA trust store.", action="store", default="./credentials/development/paa-root-certs")
parser.add_argument(
"-s", "--server-interactions", help="Enable server interactions.", action="store_true")
args = parser.parse_args()

if not os.path.exists(args.trust_store):
Expand Down Expand Up @@ -137,7 +139,7 @@ def main():

ReplInit(args.debug)

chipStack = ChipStack(persistentStoragePath=args.storagepath, enableServerInteractions=False)
chipStack = ChipStack(persistentStoragePath=args.storagepath, enableServerInteractions=args.server_interactions)
certificateAuthorityManager = chip.CertificateAuthority.CertificateAuthorityManager(chipStack, chipStack.GetStorageManager())

certificateAuthorityManager.LoadAuthoritiesFromStorage()
Expand Down
39 changes: 39 additions & 0 deletions src/controller/python/chip/icd/CheckInDelegate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
*
* Copyright (c) 2024 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 "CheckInDelegate.h"

using namespace ::chip;
using namespace ::chip::app;

PyChipCheckInDelegate PyChipCheckInDelegate::sInstance;

void PyChipCheckInDelegate::OnCheckInComplete(const ICDClientInfo & clientInfo)
{
DefaultCheckInDelegate::OnCheckInComplete(clientInfo);

if (mCallback != nullptr)
{
mCallback(clientInfo.peer_node.GetFabricIndex(), clientInfo.peer_node.GetNodeId());
}
}

void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback)
{
PyChipCheckInDelegate::GetInstance().SetOnCheckInCompleteCallback(callback);
}
40 changes: 40 additions & 0 deletions src/controller/python/chip/icd/CheckInDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
*
* Copyright (c) 2024 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 <app/icd/client/DefaultCheckInDelegate.h>

class PyChipCheckInDelegate : public chip::app::DefaultCheckInDelegate
{
public:
using OnCheckInCompleteCallback = void(uint8_t fabricIndex, uint64_t nodeId);

virtual ~PyChipCheckInDelegate() = default;

void OnCheckInComplete(const chip::app::ICDClientInfo & clientInfo) override;

void SetOnCheckInCompleteCallback(OnCheckInCompleteCallback * callback) { mCallback = callback; }

static PyChipCheckInDelegate & GetInstance() { return sInstance; }

private:
static PyChipCheckInDelegate sInstance;

OnCheckInCompleteCallback * mCallback;
};
Loading

0 comments on commit 4b9334f

Please sign in to comment.