[Python] Add Python commissioning flow (#25119)
* [Python] Add python commissioning flow
* Update
* Update
* Format
* Fix
* Fix
* Update
---------
Co-authored-by: yunhanw-google <yunhanw@google.com>
diff --git a/src/controller/python/OpCredsBinding.cpp b/src/controller/python/OpCredsBinding.cpp
index c7f3e1d..3c4da74 100644
--- a/src/controller/python/OpCredsBinding.cpp
+++ b/src/controller/python/OpCredsBinding.cpp
@@ -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>
@@ -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>
@@ -57,6 +59,8 @@
return &attestationTrustStore;
}
+
+chip::Python::PlaceholderOperationalCredentialsIssuer sPlaceholderOperationalCredentialsIssuer;
} // namespace
namespace chip {
@@ -369,11 +373,74 @@
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));
@@ -393,8 +460,18 @@
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));
@@ -419,19 +496,21 @@
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)
{
@@ -505,6 +584,22 @@
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();