Skip to content

Commit

Permalink
[Python] Add Python commissioning flow (#25119)
Browse files Browse the repository at this point in the history
* [Python] Add python commissioning flow

* Update

* Update

* Format

* Fix

* Fix

* Update

---------

Co-authored-by: yunhanw-google <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Oct 16, 2023
1 parent 4645c9d commit 4380642
Show file tree
Hide file tree
Showing 20 changed files with 1,855 additions and 181 deletions.
1 change: 1 addition & 0 deletions scripts/tests/cirque_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ CIRQUE_TESTS=(
"SplitCommissioningTest"
"CommissioningFailureTest"
"CommissioningFailureOnReportTest"
"PythonCommissioningTest"
"CommissioningWindowTest"
)

Expand Down
11 changes: 11 additions & 0 deletions src/controller/python/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ shared_library("ChipDeviceCtrl") {
"OpCredsBinding.cpp",
"chip/clusters/attribute.cpp",
"chip/clusters/command.cpp",
"chip/credentials/cert.cpp",
"chip/crypto/p256keypair.cpp",
"chip/discovery/NodeResolution.cpp",
"chip/interaction_model/Delegate.cpp",
"chip/interaction_model/Delegate.h",
Expand Down Expand Up @@ -213,7 +215,13 @@ chip_python_wheel_action("chip-core") {
"chip/clusters/Command.py",
"chip/clusters/__init__.py",
"chip/clusters/enum.py",
"chip/commissioning/__init__.py",
"chip/commissioning/commissioning_flow_blocks.py",
"chip/commissioning/pase.py",
"chip/configuration/__init__.py",
"chip/credentials/cert.py",
"chip/crypto/fabric.py",
"chip/crypto/p256keypair.py",
"chip/discovery/__init__.py",
"chip/discovery/library_handle.py",
"chip/discovery/types.py",
Expand Down Expand Up @@ -270,7 +278,10 @@ chip_python_wheel_action("chip-core") {
"chip.ble",
"chip.ble.commissioning",
"chip.configuration",
"chip.commissioning",
"chip.clusters",
"chip.credentials",
"chip.crypto",
"chip.utils",
"chip.discovery",
"chip.exceptions",
Expand Down
121 changes: 108 additions & 13 deletions src/controller/python/OpCredsBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "ChipDeviceController-ScriptDevicePairingDelegate.h"
#include "ChipDeviceController-StorageDelegate.h"

#include "controller/python/chip/crypto/p256keypair.h"
#include "controller/python/chip/interaction_model/Delegate.h"

#include <controller/CHIPDeviceController.h>
Expand All @@ -36,6 +37,7 @@
#include <lib/support/TestGroupData.h>
#include <lib/support/logging/CHIPLogging.h>

#include <controller/python/chip/commissioning/PlaceholderOperationalCredentialsIssuer.h>
#include <controller/python/chip/native/PyChipError.h>
#include <credentials/GroupDataProviderImpl.h>
#include <credentials/attestation_verifier/DefaultDeviceAttestationVerifier.h>
Expand All @@ -57,6 +59,8 @@ const chip::Credentials::AttestationTrustStore * GetTestFileAttestationTrustStor

return &attestationTrustStore;
}

chip::Python::PlaceholderOperationalCredentialsIssuer sPlaceholderOperationalCredentialsIssuer;
} // namespace

namespace chip {
Expand Down Expand Up @@ -369,11 +373,74 @@ void pychip_OnCommissioningStatusUpdate(chip::PeerId peerId, chip::Controller::C
return sTestCommissioner.OnCommissioningStatusUpdate(peerId, stageCompleted, err);
}

/**
* Allocates a controller that does not use auto-commisioning.
*
* TODO(#25214): Need clean up API
*
*/
PyChipError pychip_OpCreds_AllocateControllerForPythonCommissioningFLow(chip::Controller::DeviceCommissioner ** outDevCtrl,
chip::python::pychip_P256Keypair * operationalKey,
uint8_t * noc, uint32_t nocLen, uint8_t * icac,
uint32_t icacLen, uint8_t * rcac, uint32_t rcacLen,
const uint8_t * ipk, uint32_t ipkLen,
chip::VendorId adminVendorId, bool enableServerInteractions)
{
ReturnErrorCodeIf(nocLen > Controller::kMaxCHIPDERCertLength, ToPyChipError(CHIP_ERROR_NO_MEMORY));
ReturnErrorCodeIf(icacLen > Controller::kMaxCHIPDERCertLength, ToPyChipError(CHIP_ERROR_NO_MEMORY));
ReturnErrorCodeIf(rcacLen > Controller::kMaxCHIPDERCertLength, ToPyChipError(CHIP_ERROR_NO_MEMORY));

ChipLogDetail(Controller, "Creating New Device Controller");

auto devCtrl = std::make_unique<chip::Controller::DeviceCommissioner>();
VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_NO_MEMORY));

Controller::SetupParams initParams;
initParams.pairingDelegate = &sPairingDelegate;
initParams.operationalCredentialsDelegate = &sPlaceholderOperationalCredentialsIssuer;
initParams.operationalKeypair = operationalKey;
initParams.controllerRCAC = ByteSpan(rcac, rcacLen);
initParams.controllerICAC = ByteSpan(icac, icacLen);
initParams.controllerNOC = ByteSpan(noc, nocLen);
initParams.enableServerInteractions = enableServerInteractions;
initParams.controllerVendorId = adminVendorId;
initParams.permitMultiControllerFabrics = true;
initParams.hasExternallyOwnedOperationalKeypair = true;

CHIP_ERROR err = Controller::DeviceControllerFactory::GetInstance().SetupCommissioner(initParams, *devCtrl);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

// Setup IPK in Group Data Provider for controller after Commissioner init which sets-up the fabric table entry
uint8_t compressedFabricId[sizeof(uint64_t)] = { 0 };
chip::MutableByteSpan compressedFabricIdSpan(compressedFabricId);

err = devCtrl->GetCompressedFabricIdBytes(compressedFabricIdSpan);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

ChipLogProgress(Support, "Setting up group data for Fabric Index %u with Compressed Fabric ID:",
static_cast<unsigned>(devCtrl->GetFabricIndex()));
ChipLogByteSpan(Support, compressedFabricIdSpan);

chip::ByteSpan fabricIpk =
(ipk == nullptr) ? chip::GroupTesting::DefaultIpkValue::GetDefaultIpk() : chip::ByteSpan(ipk, ipkLen);
err =
chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, devCtrl->GetFabricIndex(), fabricIpk, compressedFabricIdSpan);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

*outDevCtrl = devCtrl.release();

return ToPyChipError(CHIP_NO_ERROR);
}

// TODO(#25214): Need clean up API
PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Controller::DeviceCommissioner ** outDevCtrl,
FabricId fabricId, chip::NodeId nodeId, chip::VendorId adminVendorId,
const char * paaTrustStorePath, bool useTestCommissioner,
bool enableServerInteractions, CASEAuthTag * caseAuthTags, uint32_t caseAuthTagLen)
bool enableServerInteractions, CASEAuthTag * caseAuthTags, uint32_t caseAuthTagLen,
chip::python::pychip_P256Keypair * operationalKey)
{
CHIP_ERROR err = CHIP_NO_ERROR;

ChipLogDetail(Controller, "Creating New Device Controller");

VerifyOrReturnError(context != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
Expand All @@ -393,8 +460,18 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co
SetDeviceAttestationVerifier(GetDefaultDACVerifier(testingRootStore));

chip::Crypto::P256Keypair ephemeralKey;
CHIP_ERROR err = ephemeralKey.Initialize(chip::Crypto::ECPKeyTarget::ECDSA);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));
chip::Crypto::P256Keypair * controllerKeyPair;

if (operationalKey == nullptr)
{
err = ephemeralKey.Initialize(chip::Crypto::ECPKeyTarget::ECDSA);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));
controllerKeyPair = &ephemeralKey;
}
else
{
controllerKeyPair = operationalKey;
}

chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
ReturnErrorCodeIf(!noc.Alloc(Controller::kMaxCHIPDERCertLength), ToPyChipError(CHIP_ERROR_NO_MEMORY));
Expand All @@ -419,19 +496,21 @@ PyChipError pychip_OpCreds_AllocateController(OpCredsContext * context, chip::Co

memcpy(catValues.values.data(), caseAuthTags, caseAuthTagLen * sizeof(CASEAuthTag));

err = context->mAdapter->GenerateNOCChain(nodeId, fabricId, catValues, ephemeralKey.Pubkey(), rcacSpan, icacSpan, nocSpan);
err =
context->mAdapter->GenerateNOCChain(nodeId, fabricId, catValues, controllerKeyPair->Pubkey(), rcacSpan, icacSpan, nocSpan);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

Controller::SetupParams initParams;
initParams.pairingDelegate = &sPairingDelegate;
initParams.operationalCredentialsDelegate = context->mAdapter.get();
initParams.operationalKeypair = &ephemeralKey;
initParams.controllerRCAC = rcacSpan;
initParams.controllerICAC = icacSpan;
initParams.controllerNOC = nocSpan;
initParams.enableServerInteractions = enableServerInteractions;
initParams.controllerVendorId = adminVendorId;
initParams.permitMultiControllerFabrics = true;
initParams.pairingDelegate = &sPairingDelegate;
initParams.operationalCredentialsDelegate = context->mAdapter.get();
initParams.operationalKeypair = controllerKeyPair;
initParams.controllerRCAC = rcacSpan;
initParams.controllerICAC = icacSpan;
initParams.controllerNOC = nocSpan;
initParams.enableServerInteractions = enableServerInteractions;
initParams.controllerVendorId = adminVendorId;
initParams.permitMultiControllerFabrics = true;
initParams.hasExternallyOwnedOperationalKeypair = operationalKey != nullptr;

if (useTestCommissioner)
{
Expand Down Expand Up @@ -505,6 +584,22 @@ PyChipError pychip_DeviceController_DeleteDeviceController(chip::Controller::Dev
return ToPyChipError(CHIP_NO_ERROR);
}

PyChipError pychip_DeviceController_SetIpk(chip::Controller::DeviceCommissioner * devCtrl, const uint8_t * ipk, size_t ipkLen)
{
VerifyOrReturnError(ipk != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));

uint8_t compressedFabricId[sizeof(uint64_t)] = { 0 };
chip::MutableByteSpan compressedFabricIdSpan(compressedFabricId);

CHIP_ERROR err = devCtrl->GetCompressedFabricIdBytes(compressedFabricIdSpan);
VerifyOrReturnError(err == CHIP_NO_ERROR, ToPyChipError(err));

err = chip::Credentials::SetSingleIpkEpochKey(&sGroupDataProvider, devCtrl->GetFabricIndex(), ByteSpan(ipk, ipkLen),
compressedFabricIdSpan);

return ToPyChipError(err);
}

bool pychip_TestCommissionerUsed()
{
return sTestCommissioner.GetTestCommissionerUsed();
Expand Down
Loading

0 comments on commit 4380642

Please sign in to comment.