Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move AddNOC and UpdateNOC to util functions for chip-repl #20430

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
92 changes: 92 additions & 0 deletions src/controller/python/chip/utils/CommissioningUtils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#
# 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.
#

tehampson marked this conversation as resolved.
Show resolved Hide resolved
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):
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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):
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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))
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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))
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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
tehampson marked this conversation as resolved.
Show resolved Hide resolved

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
tehampson marked this conversation as resolved.
Show resolved Hide resolved
# 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."""
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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()
tehampson marked this conversation as resolved.
Show resolved Hide resolved
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