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

Implement TC-SC-3.6 as a Python test #21719

Merged
merged 3 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions src/controller/python/chip-device-ctrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ def do_setuppayload(self, line):
setup-payload generate [options]

Options:
-vr Version
-vr Version
-vi Vendor ID
-pi Product ID
-cf Custom Flow [Standard = 0, UserActionRequired = 1, Custom = 2]
Expand Down Expand Up @@ -971,7 +971,7 @@ def do_opencommissioningwindow(self, line):
open-commissioning-window <nodeid> [options]

Options:
-t Timeout (in seconds)
-t Timeout (in seconds)
-o Option [TokenWithRandomPIN = 1, TokenWithProvidedPIN = 2]
-d Discriminator Value
-i Iteration
Expand Down Expand Up @@ -1024,7 +1024,7 @@ def do_getfabricid(self, line):
return

compressed_fabricid = self.devCtrl.GetCompressedFabricId()
raw_fabricid = self.devCtrl.GetFabricId()
raw_fabricid = self.devCtrl.fabricId
except exceptions.ChipStackException as ex:
print("An exception occurred during reading FabricID:")
print(str(ex))
Expand Down
67 changes: 53 additions & 14 deletions src/controller/python/chip/ChipDeviceCtrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@
from .clusters import Objects as GeneratedObjects
from .clusters.CHIPClusters import *
from . import clusters as Clusters
from .FabricAdmin import FabricAdmin
import enum
import threading
import typing
import builtins
import ipdb
import ctypes
import copy

Expand Down Expand Up @@ -157,7 +157,7 @@ class DiscoveryFilterType(enum.IntEnum):
class ChipDeviceController():
activeList = set()

def __init__(self, opCredsContext: ctypes.c_void_p, fabricId: int, nodeId: int, adminVendorId: int, paaTrustStorePath: str = "", useTestCommissioner: bool = False):
def __init__(self, opCredsContext: ctypes.c_void_p, fabricId: int, nodeId: int, adminVendorId: int, paaTrustStorePath: str = "", useTestCommissioner: bool = False, fabricAdmin: FabricAdmin = None, name: str = None):
self.state = DCState.NOT_INITIALIZED
self.devCtrl = None
self._ChipStack = builtins.chipStack
Expand All @@ -174,19 +174,23 @@ def __init__(self, opCredsContext: ctypes.c_void_p, fabricId: int, nodeId: int,
opCredsContext), pointer(devCtrl), fabricId, nodeId, adminVendorId, ctypes.c_char_p(None if len(paaTrustStorePath) == 0 else str.encode(paaTrustStorePath)), useTestCommissioner)
)

self.nodeId = nodeId

if res != 0:
raise self._ChipStack.ErrorToException(res)

self.devCtrl = devCtrl
self._fabricAdmin = fabricAdmin
self._fabricId = fabricId
self._nodeId = nodeId
self._adminIndex = fabricAdmin.adminIndex

if name is None:
self._name = "adminIndex(%x)/fabricId(0x%016X)/nodeId(0x%016X)" % (fabricAdmin.adminIndex, fabricId, nodeId)
else:
self._name = name

self._Cluster = ChipClusters(builtins.chipStack)
self._Cluster.InitLib(self._dmLib)

def GetNodeId(self):
return self.nodeId

def HandleCommissioningComplete(nodeid, err):
if err != 0:
print("Failed to commission: {}".format(err))
Expand All @@ -198,7 +202,7 @@ def HandleCommissioningComplete(nodeid, err):
self._ChipStack.commissioningCompleteEvent.set()
self._ChipStack.completeEvent.set()

def HandleKeyExchangeComplete(err):
def HandlePASEEstablishmentComplete(err):
if err != 0:
print("Failed to establish secure session to device: {}".format(err))
self._ChipStack.callbackRes = self._ChipStack.ErrorToException(
Expand All @@ -207,7 +211,7 @@ def HandleKeyExchangeComplete(err):
print("Established secure session with Device")

if self.state != DCState.COMMISSIONING:
# During Commissioning, HandleKeyExchangeComplete will also be called,
# During Commissioning, HandlePASEEstablishmentComplete will also be called,
# in this case the async operation should be marked as finished by
# HandleCommissioningComplete instead this function.
self.state = DCState.IDLE
Expand All @@ -218,10 +222,10 @@ def HandleKeyExchangeComplete(err):
if err != 0:
HandleCommissioningComplete(0, err)

self.cbHandleKeyExchangeCompleteFunct = _DevicePairingDelegate_OnPairingCompleteFunct(
HandleKeyExchangeComplete)
self.cbHandlePASEEstablishmentCompleteFunct = _DevicePairingDelegate_OnPairingCompleteFunct(
HandlePASEEstablishmentComplete)
self._dmLib.pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(
self.devCtrl, self.cbHandleKeyExchangeCompleteFunct)
self.devCtrl, self.cbHandlePASEEstablishmentCompleteFunct)

self.cbHandleCommissioningCompleteFunct = _DevicePairingDelegate_OnCommissioningCompleteFunct(
HandleCommissioningComplete)
Expand All @@ -231,8 +235,40 @@ def HandleKeyExchangeComplete(err):
self.state = DCState.IDLE
self.isActive = True

# Validate FabricID/NodeID followed from NOC Chain
self._fabricId = self.GetFabricIdInternal()
assert self._fabricId == fabricId
self._nodeId = self.GetNodeIdInternal()
assert self._nodeId == nodeId

ChipDeviceController.activeList.add(self)

@property
def fabricAdmin(self) -> FabricAdmin:
return self._fabricAdmin

@property
def nodeId(self) -> int:
self.CheckIsActive()
return self._nodeId

@property
def fabricId(self) -> int:
self.CheckIsActive()
return self._fabricId

@property
def adminIndex(self) -> int:
return self._adminIndex

@property
def name(self) -> str:
return self._name

@name.setter
def name(self, new_name: str):
self._name = new_name

def Shutdown(self):
''' Shuts down this controller and reclaims any used resources, including the bound
C++ constructor instance in the SDK.
Expand Down Expand Up @@ -447,6 +483,7 @@ def CommissionWithCode(self, setupPayload: str, nodeid: int):
return self._ChipStack.commissioningEventRes == 0

def CommissionIP(self, ipaddr: str, setupPinCode: int, nodeid: int):
""" DEPRECATED, DO NOT USE! Use `CommissionOnNetwork` or `CommissionWithCode` """
tcarmelveilleux marked this conversation as resolved.
Show resolved Hide resolved
self.CheckIsActive()

# IP connection will run through full commissioning, so we need to wait
Expand Down Expand Up @@ -614,7 +651,8 @@ def GetCompressedFabricId(self):
else:
raise self._ChipStack.ErrorToException(res)

def GetFabricId(self):
def GetFabricIdInternal(self):
"""Get the fabric ID from the object. Only used to validate cached value from property."""
self.CheckIsActive()

fabricid = c_uint64(0)
Expand All @@ -629,7 +667,8 @@ def GetFabricId(self):
else:
raise self._ChipStack.ErrorToException(res)

def GetNodeId(self):
def GetNodeIdInternal(self) -> int:
"""Get the node ID from the object. Only used to validate cached value from property."""
self.CheckIsActive()

nodeid = c_uint64(0)
Expand Down
17 changes: 14 additions & 3 deletions src/controller/python/chip/FabricAdmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from typing import *
from ctypes import *
from rich.pretty import pprint
import ipdb
import json
import logging
import builtins
Expand Down Expand Up @@ -102,7 +101,7 @@ def __init__(self, vendorId: int, adminIndex: int = None, fabricId: int = 1):
raise ValueError(
f"Invalid VendorID ({vendorId}) provided!")

self.vendorId = vendorId
self._vendorId = vendorId
self._fabricId = fabricId

if (adminIndex is None):
Expand Down Expand Up @@ -160,7 +159,7 @@ def NewController(self, nodeId: int = None, paaTrustStorePath: str = "", useTest
f"Allocating new controller with FabricId: 0x{self._fabricId:016X}, NodeId: 0x{nodeId:016X}")

controller = ChipDeviceCtrl.ChipDeviceController(
self.closure, self._fabricId, nodeId, self.vendorId, paaTrustStorePath, useTestCommissioner)
self.closure, self._fabricId, nodeId, self.vendorId, paaTrustStorePath, useTestCommissioner, fabricAdmin=self)
return controller

def ShutdownAll():
Expand Down Expand Up @@ -200,3 +199,15 @@ def Shutdown(self, deleteFromStorage: bool = True):

def __del__(self):
self.Shutdown(False)

@property
def vendorId(self) -> int:
return self._vendorId

@property
def fabricId(self) -> int:
return self._fabricId

@property
def adminIndex(self) -> int:
return self._adminIndex
6 changes: 3 additions & 3 deletions src/controller/python/chip/clusters/Attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ def UpdateCachedData(self):


class SubscriptionTransaction:
def __init__(self, transaction: 'AsyncReadTransaction', subscriptionId, devCtrl):
def __init__(self, transaction: AsyncReadTransaction, subscriptionId, devCtrl):
self._onResubscriptionAttemptedCb = DefaultResubscriptionAttemptedCallback
self._onAttributeChangeCb = DefaultAttributeChangeCallback
self._onEventChangeCb = DefaultEventChangeCallback
Expand Down Expand Up @@ -760,9 +760,9 @@ def _handleDone(self):
if not self._future.done():
if self._resultError:
if self._subscription_handler:
self._subscription_handler.OnErrorCb(chipError, self._subscription_handler)
self._subscription_handler.OnErrorCb(self._resultError, self._subscription_handler)
else:
self._future.set_exception(chip.exceptions.ChipStackError(chipError))
self._future.set_exception(chip.exceptions.ChipStackError(self._resultError))
else:
self._future.set_result(AsyncReadTransaction.ReadResponse(
attributes=self._cache.attributeCache, events=self._events))
Expand Down
1 change: 0 additions & 1 deletion src/controller/python/chip/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from typing import *
from ctypes import *
from rich.pretty import pprint
import ipdb
import json
import logging
import base64
Expand Down
35 changes: 16 additions & 19 deletions src/controller/python/chip/utils/CommissioningBuildingBlocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ async def GrantPrivilege(adminCtrl: ChipDeviceController, grantedCtrl: ChipDevic
# Step 1: Wipe the subject from all existing ACLs.
for acl in currentAcls:
if (acl.subjects != NullValue):
acl.subjects = [subject for subject in acl.subjects if subject != grantedCtrl.GetNodeId()]
acl.subjects = [subject for subject in acl.subjects if subject != grantedCtrl.nodeId]

if (privilege):
addedPrivilege = False
Expand All @@ -75,8 +75,8 @@ async def GrantPrivilege(adminCtrl: ChipDeviceController, grantedCtrl: ChipDevic
# the existing privilege in that entry matches our desired privilege.
for acl in currentAcls:
if acl.privilege == privilege:
if grantedCtrl.GetNodeId() not in acl.subjects:
acl.subjects.append(grantedCtrl.GetNodeId())
if grantedCtrl.nodeId not in acl.subjects:
acl.subjects.append(grantedCtrl.nodeId)
addedPrivilege = True

# Step 3: If there isn't an existing entry to add to, make a new one.
Expand All @@ -86,7 +86,7 @@ async def GrantPrivilege(adminCtrl: ChipDeviceController, grantedCtrl: ChipDevic
f"Cannot add another ACL entry to grant privilege to existing count of {currentAcls} ACLs -- will exceed minimas!")

currentAcls.append(Clusters.AccessControl.Structs.AccessControlEntry(privilege=privilege, authMode=Clusters.AccessControl.Enums.AuthMode.kCase,
subjects=[grantedCtrl.GetNodeId()]))
subjects=[grantedCtrl.nodeId]))

# Step 4: Prune ACLs which have empty subjects.
currentAcls = [acl for acl in currentAcls if acl.subjects != NullValue and len(acl.subjects) != 0]
Expand Down Expand Up @@ -115,24 +115,21 @@ async def CreateControllersOnFabric(fabricAdmin: FabricAdmin, adminDevCtrl: Chip


async def AddNOCForNewFabricFromExisting(commissionerDevCtrl, newFabricDevCtrl, existingNodeId, newNodeId):
''' Perform sequence to commission new frabric using existing commissioned fabric.
''' Perform sequence to commission new fabric using existing commissioned fabric.

Args:
commissionerDevCtrl (ChipDeviceController): Already commissioned device controller used
to commission a new fabric on `newFabricDevCtrl`.
newFabricDevCtrl (ChipDeviceController): New device controller which is used for the new
fabric we are establishing.
existingNodeId (int): Node ID of the server we are establishing a CASE session on the
existing fabric that we will used to perform AddNOC.
newNodeId (int): Node ID that we would like to server to used on the new fabric being
added.
existingNodeId (int): Node ID of the target where an AddNOC needs to be done for a new fabric.
newNodeId (int): Node ID to use for the target node on the new fabric.

Return:
bool: True if successful, False otherwise.

'''

resp = await commissionerDevCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(60), timedRequestTimeoutMs=1000)
resp = await commissionerDevCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(60))
if resp.errorCode is not generalCommissioning.Enums.CommissioningError.kOk:
return False

Expand All @@ -141,20 +138,20 @@ async def AddNOCForNewFabricFromExisting(commissionerDevCtrl, newFabricDevCtrl,
chainForAddNOC = newFabricDevCtrl.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 commissionerDevCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
await commissionerDevCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0))
return False

await commissionerDevCtrl.SendCommand(existingNodeId, 0, opCreds.Commands.AddTrustedRootCertificate(chainForAddNOC.rcacBytes))
resp = await commissionerDevCtrl.SendCommand(existingNodeId, 0, opCreds.Commands.AddNOC(chainForAddNOC.nocBytes, chainForAddNOC.icacBytes, chainForAddNOC.ipkBytes, newFabricDevCtrl.GetNodeId(), 0xFFF1))
resp = await commissionerDevCtrl.SendCommand(existingNodeId, 0, opCreds.Commands.AddNOC(chainForAddNOC.nocBytes, chainForAddNOC.icacBytes, chainForAddNOC.ipkBytes, newFabricDevCtrl.nodeId, 0xFFF1))
if resp.statusCode is not opCreds.Enums.OperationalCertStatus.kSuccess:
# Expiring the failsafe timer in an attempt to clean up.
await commissionerDevCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
await commissionerDevCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0))
return False

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

if not await _IsNodeInFabricList(newFabricDevCtrl, newNodeId):
Expand All @@ -179,20 +176,20 @@ async def UpdateNOC(devCtrl, existingNodeId, newNodeId):
bool: True if successful, False otherwise.

"""
resp = await devCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(600), timedRequestTimeoutMs=1000)
resp = await devCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(600))
if resp.errorCode is not generalCommissioning.Enums.CommissioningError.kOk:
return False
csrForUpdateNOC = await devCtrl.SendCommand(
existingNodeId, 0, opCreds.Commands.CSRRequest(CSRNonce=os.urandom(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, generalCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
await devCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0))
return False

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

# Forget our session since the peer deleted it
Expand All @@ -201,7 +198,7 @@ async def UpdateNOC(devCtrl, existingNodeId, newNodeId):
resp = await devCtrl.SendCommand(newNodeId, 0, generalCommissioning.Commands.CommissioningComplete())
if resp.errorCode is not generalCommissioning.Enums.CommissioningError.kOk:
# Expiring the failsafe timer in an attempt to clean up.
await devCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0), timedRequestTimeoutMs=1000)
await devCtrl.SendCommand(existingNodeId, 0, generalCommissioning.Commands.ArmFailSafe(0))
return False

if not await _IsNodeInFabricList(devCtrl, newNodeId):
Expand Down
Loading