Skip to content

Commit

Permalink
Move AddNOC and UpdateNOC to util functions that can be called using …
Browse files Browse the repository at this point in the history
…chip-repl
  • Loading branch information
tehampson committed Jul 7, 2022
1 parent a9da0d8 commit f0ca805
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 75 deletions.
2 changes: 2 additions & 0 deletions src/controller/python/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ pw_python_action("python") {
"chip/clusters/TestObjects.py",
"chip/clusters/Types.py",
"chip/clusters/__init__.py",
"chip/utils/__init__.py",
"chip/utils/CommissioningUtils.py",
"chip/configuration/__init__.py",
"chip/discovery/__init__.py",
"chip/discovery/library_handle.py",
Expand Down
1 change: 1 addition & 0 deletions src/controller/python/build-chip-wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def finalize_options(self):
'chip.ble.commissioning',
'chip.configuration',
'chip.clusters',
'chip.utils',
'chip.discovery',
'chip.exceptions',
'chip.internal',
Expand Down
1 change: 1 addition & 0 deletions src/controller/python/chip/ChipReplStartup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import argparse
import builtins
import chip.FabricAdmin
from chip.utils import CommissioningUtils
import atexit

_fabricAdmins = None
Expand Down
90 changes: 90 additions & 0 deletions src/controller/python/chip/utils/CommissioningUtils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#
# Copyright (c) 2022 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.
#

import asyncio
import chip.clusters as Clusters
import chip.tlv

_UINT16_MAX = 65535

async def _IsBasicAttributeReadSuccessful(devCtrl, nodeId):
resp = await devCtrl.ReadAttribute(nodeId, [(Clusters.Basic.Attributes.ClusterRevision)])
clusterRevision = resp[0][Clusters.Basic][Clusters.Basic.Attributes.ClusterRevision]
if not isinstance(clusterRevision, chip.tlv.uint) or clusterRevision < 1 or clusterRevision > _UINT16_MAX:
return False
return True


async def AddNOC(commissionedDevCtrl, newDevCtrl, existingNodeId, newNodeId):
resp = await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(60), timedRequestTimeoutMs=1000)
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
return False

csrForAddNOC = await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.OperationalCredentials.Commands.CSRRequest(CSRNonce=b'1' * 32))

chainForAddNOC = newDevCtrl.IssueNOCChain(csrForAddNOC, newNodeId)
if chainForAddNOC.rcacBytes is None or chainForAddNOC.icacBytes is None or chainForAddNOC.nocBytes is None or chainForAddNOC.ipkBytes is None:
# Expiring the failsafe timer in an attempt to clean up.
await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
return False

# TODO error check on the commands below
await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.OperationalCredentials.Commands.AddTrustedRootCertificate(chainForAddNOC.rcacBytes))
resp = await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.OperationalCredentials.Commands.AddNOC(chainForAddNOC.nocBytes, chainForAddNOC.icacBytes, chainForAddNOC.ipkBytes, newDevCtrl.GetNodeId(), 0xFFF1))
if resp.statusCode is not Clusters.OperationalCredentials.Enums.OperationalCertStatus.kSuccess:
# Expiring the failsafe timer in an attempt to clean up.
await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
return False

resp = await newDevCtrl.SendCommand(newNodeId, 0, Clusters.GeneralCommissioning.Commands.CommissioningComplete())
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
# Expiring the failsafe timer in an attempt to clean up.
await commissionedDevCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
return False

if not await _IsBasicAttributeReadSuccessful(newDevCtrl, newNodeId):
return False

return True

async def UpdateNOC(devCtrl, existingNodeId, newNodeId):
resp = await devCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(600), timedRequestTimeoutMs=1000)
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
return False
csrForUpdateNOC = await devCtrl.SendCommand(
existingNodeId, 0, Clusters.OperationalCredentials.Commands.CSRRequest(CSRNonce=b'1' * 32, isForUpdateNOC=True))
chainForUpdateNOC = devCtrl.IssueNOCChain(csrForUpdateNOC, newNodeId)
if chainForUpdateNOC.rcacBytes is None or chainForUpdateNOC.icacBytes is None or chainForUpdateNOC.nocBytes is None or chainForUpdateNOC.ipkBytes is None:
await devCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
return False

resp = await devCtrl.SendCommand(existingNodeId, 0, Clusters.OperationalCredentials.Commands.UpdateNOC(chainForUpdateNOC.nocBytes, chainForUpdateNOC.icacBytes))
if resp.statusCode is not Clusters.OperationalCredentials.Enums.OperationalCertStatus.kSuccess:
# Expiring the failsafe timer in an attempt to clean up.
await devCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
return False

resp = await devCtrl.SendCommand(newNodeId, 0, Clusters.GeneralCommissioning.Commands.CommissioningComplete())
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
# Expiring the failsafe timer in an attempt to clean up.
await devCtrl.SendCommand(existingNodeId, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
return False

if not await _IsBasicAttributeReadSuccessful(devCtrl, newNodeId):
return False

return True
24 changes: 24 additions & 0 deletions src/controller/python/chip/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# 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.
#

#
# @file
# Provides Python APIs for CHIP.
#

"""Provides Python APIs for CHIP."""
from . import CommissioningUtils
93 changes: 18 additions & 75 deletions src/controller/python/test/test_scripts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import ctypes
import chip.clusters as Clusters
import chip.clusters.Attribute as Attribute
from chip.utils import CommissioningUtils
from chip.ChipStack import *
import chip.FabricAdmin
import copy
Expand Down Expand Up @@ -63,8 +64,6 @@ def FailIfNot(cond, message):
_enabled_tests = []
_disabled_tests = []

_UINT16_MAX = 65535


def SetTestSet(enabled_tests, disabled_tests):
global _enabled_tests, _disabled_tests
Expand Down Expand Up @@ -369,101 +368,45 @@ async def TestAddUpdateRemoveFabric(self, nodeid: int):
self.logger.info("Waiting for attribute read for CommissionedFabrics")
startOfTestFabricCount = await self._GetCommissonedFabricCount(nodeid)

tempFabric = chip.FabricAdmin.FabricAdmin(fabricId=3, fabricIndex=3)
tempFabric = chip.FabricAdmin.FabricAdmin()
tempDevCtrl = tempFabric.NewController(self.controllerNodeId, self.paaTrustStorePath)

self.logger.info("Setting failsafe on CASE connection")
resp = await self.devCtrl.SendCommand(nodeid, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(60), timedRequestTimeoutMs=1000)
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
self.logger.error(
"Incorrect response received from arm failsafe - wanted OK, received {}".format(resp))
return False

csrForAddNOC = await self.devCtrl.SendCommand(nodeid, 0, Clusters.OperationalCredentials.Commands.CSRRequest(CSRNonce=b'1' * 32))

chainForAddNOC = tempDevCtrl.IssueNOCChain(csrForAddNOC, nodeid)
if chainForAddNOC.rcacBytes is None or chainForAddNOC.icacBytes is None or chainForAddNOC.nocBytes is None or chainForAddNOC.ipkBytes is None:
self.logger.error("IssueNOCChain failed to generate valid cert chain for AddNOC")
self.logger.info("Starting AddNOC using same node ID")
if not await CommissioningUtils.AddNOC(self.devCtrl, tempDevCtrl, nodeid, nodeid):
self.logger.error("AddNOC failed")
return False

self.logger.info("Starting AddNOC portion of test")
# TODO error check on the commands below
await self.devCtrl.SendCommand(nodeid, 0, Clusters.OperationalCredentials.Commands.AddTrustedRootCertificate(chainForAddNOC.rcacBytes))
await self.devCtrl.SendCommand(nodeid, 0, Clusters.OperationalCredentials.Commands.AddNOC(chainForAddNOC.nocBytes, chainForAddNOC.icacBytes, chainForAddNOC.ipkBytes, tempDevCtrl.GetNodeId(), 0xFFF1))
await tempDevCtrl.SendCommand(nodeid, 0, Clusters.GeneralCommissioning.Commands.CommissioningComplete())

afterAddNocFabricCount = await self._GetCommissonedFabricCount(nodeid)
if startOfTestFabricCount == afterAddNocFabricCount:
expectedFabricCountUntilRemoveFabric = startOfTestFabricCount + 1
if expectedFabricCountUntilRemoveFabric != await self._GetCommissonedFabricCount(nodeid):
self.logger.error("Expected commissioned fabric count to change after AddNOC")
return False

resp = await tempDevCtrl.ReadAttribute(nodeid, [(Clusters.Basic.Attributes.ClusterRevision)])
clusterRevision = resp[0][Clusters.Basic][Clusters.Basic.Attributes.ClusterRevision]
if not isinstance(clusterRevision, chip.tlv.uint) or clusterRevision < 1 or clusterRevision > _UINT16_MAX:
self.logger.error(
"Failed to read valid cluster revision on newly added fabric {}".format(resp))
return False

self.logger.info("Starting UpdateNOC using same node ID")
resp = await tempDevCtrl.SendCommand(nodeid, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(600), timedRequestTimeoutMs=1000)
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
self.logger.error(
"Incorrect response received from arm failsafe - wanted OK, received {}".format(resp))
return False
csrForUpdateNOC = await tempDevCtrl.SendCommand(
nodeid, 0, Clusters.OperationalCredentials.Commands.CSRRequest(CSRNonce=b'1' * 32, isForUpdateNOC=True))
chainForUpdateNOC = tempDevCtrl.IssueNOCChain(csrForUpdateNOC, nodeid)
if chainForUpdateNOC.rcacBytes is None or chainForUpdateNOC.icacBytes is None or chainForUpdateNOC.nocBytes is None or chainForUpdateNOC.ipkBytes is None:
self.logger.error("IssueNOCChain failed to generate valid cert chain for UpdateNOC")
if not await CommissioningUtils.UpdateNOC(tempDevCtrl, nodeid, nodeid):
self.logger.error("UpdateNOC using same node ID failed")
return False

await tempDevCtrl.SendCommand(nodeid, 0, Clusters.OperationalCredentials.Commands.UpdateNOC(chainForUpdateNOC.nocBytes, chainForUpdateNOC.icacBytes))
await tempDevCtrl.SendCommand(nodeid, 0, Clusters.GeneralCommissioning.Commands.CommissioningComplete())
afterUpdateNocFabricCount = await self._GetCommissonedFabricCount(nodeid)
if afterUpdateNocFabricCount != afterAddNocFabricCount:
if expectedFabricCountUntilRemoveFabric != await self._GetCommissonedFabricCount(nodeid):
self.logger.error("Expected commissioned fabric count to remain unchanged after UpdateNOC")
return False

resp = await tempDevCtrl.ReadAttribute(nodeid, [(Clusters.Basic.Attributes.ClusterRevision)])
clusterRevision = resp[0][Clusters.Basic][Clusters.Basic.Attributes.ClusterRevision]
if not isinstance(clusterRevision, chip.tlv.uint) or clusterRevision < 1 or clusterRevision > _UINT16_MAX:
self.logger.error(
"Failed to read valid cluster revision on after UpdateNOC {}".format(resp))
return False

self.logger.info("Starting UpdateNOC using different node ID")
resp = await tempDevCtrl.SendCommand(nodeid, 0, Clusters.GeneralCommissioning.Commands.ArmFailSafe(600), timedRequestTimeoutMs=1000)
if resp.errorCode is not Clusters.GeneralCommissioning.Enums.CommissioningError.kOk:
self.logger.error(
"Incorrect response received from arm failsafe - wanted OK, received {}".format(resp))
return False
csrForUpdateNOC = await tempDevCtrl.SendCommand(
nodeid, 0, Clusters.OperationalCredentials.Commands.CSRRequest(CSRNonce=b'1' * 32, isForUpdateNOC=True))

newNodeIdForUpdateNoc = nodeid + 1
chainForSecondUpdateNOC = tempDevCtrl.IssueNOCChain(csrForUpdateNOC, newNodeIdForUpdateNoc)
if chainForSecondUpdateNOC.rcacBytes is None or chainForSecondUpdateNOC.icacBytes is None or chainForSecondUpdateNOC.nocBytes is None or chainForSecondUpdateNOC.ipkBytes is None:
self.logger.error("IssueNOCChain failed to generate valid cert chain for UpdateNOC with new node ID")
if not await CommissioningUtils.UpdateNOC(tempDevCtrl, nodeid, newNodeIdForUpdateNoc):
self.logger.error("UpdateNOC using different node ID failed")
return False
updateNocResponse = await tempDevCtrl.SendCommand(nodeid, 0, Clusters.OperationalCredentials.Commands.UpdateNOC(chainForSecondUpdateNOC.nocBytes, chainForSecondUpdateNOC.icacBytes))
await tempDevCtrl.SendCommand(newNodeIdForUpdateNoc, 0, Clusters.GeneralCommissioning.Commands.CommissioningComplete())
afterUpdateNocWithNewNodeIdFabricCount = await self._GetCommissonedFabricCount(nodeid)
if afterUpdateNocFabricCount != afterUpdateNocWithNewNodeIdFabricCount:

if expectedFabricCountUntilRemoveFabric != await self._GetCommissonedFabricCount(nodeid):
self.logger.error("Expected commissioned fabric count to remain unchanged after UpdateNOC with new node ID")
return False

# TODO Read using old node ID and expect that it fails.
resp = await tempDevCtrl.ReadAttribute(newNodeIdForUpdateNoc, [(Clusters.Basic.Attributes.ClusterRevision)])
clusterRevision = resp[0][Clusters.Basic][Clusters.Basic.Attributes.ClusterRevision]
if not isinstance(clusterRevision, chip.tlv.uint) or clusterRevision < 1 or clusterRevision > _UINT16_MAX:
self.logger.error(
"Failed to read valid cluster revision on after UpdateNOC with new node ID {}".format(resp))
return False

removeFabricResponse = await tempDevCtrl.SendCommand(newNodeIdForUpdateNoc, 0, Clusters.OperationalCredentials.Commands.RemoveFabric(updateNocResponse.fabricIndex))
currentFabricIndexResponse = await tempDevCtrl.ReadAttribute(newNodeIdForUpdateNoc, [(Clusters.OperationalCredentials.Attributes.CurrentFabricIndex)])
updatedNOCFabricIndex = currentFabricIndexResponse[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex]
removeFabricResponse = await tempDevCtrl.SendCommand(newNodeIdForUpdateNoc, 0, Clusters.OperationalCredentials.Commands.RemoveFabric(updatedNOCFabricIndex))

endOfTestFabricCount = await self._GetCommissonedFabricCount(nodeid)
if endOfTestFabricCount != startOfTestFabricCount:
if startOfTestFabricCount != await self._GetCommissonedFabricCount(nodeid):
self.logger.error("Expected fabric count to be the same at the end of test as when it started")
return False

Expand Down

0 comments on commit f0ca805

Please sign in to comment.