Skip to content

Commit c53bbb1

Browse files
gvargas-csacecille
andauthored
TC-OPCREDS-3.4: Automate (#33541)
* chore(TC_OPCREDS_3.4): skeleton class * chore(TC_OPCREDS_3.4): implementation until step CSRRequest IsForUpdatedNOC=True * chore(TC_OPCREDS_3.4): All test step are implement using the old way to printed them * chore(TC_OPCREDS_3.4): patch from restyled code * chore(TC_OPCREDS_3.4): restyled by autopep8 * chore(TC_OPCREDS_3.4): fix code-lints * chore(TC_OPCREDS_3.4): add suggestions * chore(TC_OPCREDS_3.4): steps _method instead print each step and add suggestions * chore(TC_OPCREDS_3.4): fix restyled * chore(TC_OPCREDS_3.4): fix re-implementation validate single entry certs * chore(TC_OPCREDS_3.4): fix resyled * chore(TC_OPCREDS_3.4): reverting commissioningBuilingBlocks without icac and nocBytes * chore(TC_OPCREDS_3.4): fix matter_testing_support ModuleNotError * chore(TC_OPCREDS_3.4): added and replaced some outcomes/methods for TestSteps * chore(TC_OPCREDS_3.4): fix restyled * chore(TC_OPCREDS_3.4): fix restyled * chore(TC_OPCREDS_3.4): added suggestions * chore(TC_OPCREDS_3.4): added expected outcomes for some test step * chore(TC_OPCREDS_3.4): fix restyled * chore(TC_OPCREDS_3.4): fix f-strings without any placeholder * chore(TC_OPCREDS_3.4): implemented all expected outcomes for all steps * chore(TC_OPCREDS_3.4): fix restyled * Update src/python_testing/TC_OPCREDS_3_4.py Needs the PICS function Co-authored-by: C Freeman <cecille@google.com> --------- Co-authored-by: C Freeman <cecille@google.com>
1 parent 926ab43 commit c53bbb1

File tree

2 files changed

+313
-0
lines changed

2 files changed

+313
-0
lines changed

src/python_testing/TC_OPCREDS_3_4.py

+305
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#
2+
# Copyright (c) 2024 Project CHIP Authors
3+
# All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
# See https://github.com/project-chip/connectedhomeip/blob/master/docs/testing/python.md#defining-the-ci-test-arguments
19+
# for details about the block below.
20+
#
21+
# === BEGIN CI TEST ARGUMENTS ===
22+
# test-runner-runs:
23+
# run1:
24+
# app: ${ALL_CLUSTERS_APP}
25+
# app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json
26+
# script-args: >
27+
# --storage-path admin_storage.json
28+
# --commissioning-method on-network
29+
# --discriminator 1234
30+
# --passcode 20202021
31+
# --PICS src/app/tests/suites/certification/ci-pics-values
32+
# --trace-to json:${TRACE_TEST_JSON}.json
33+
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
34+
# factory-reset: true
35+
# quiet: true
36+
# === END CI TEST ARGUMENTS ===
37+
38+
import random
39+
40+
import chip.clusters as Clusters
41+
from chip.interaction_model import InteractionModelError, Status
42+
from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
43+
from mobly import asserts
44+
from test_plan_support import commission_if_required, read_attribute, send_command
45+
46+
47+
def verify_noc() -> str:
48+
return ("- Verify that the returned list has a single entry.\n"
49+
"- Save the NOC field as noc_original and the ICAC field as icac_original.\n")
50+
51+
52+
def verify_trusted_root_original() -> str:
53+
return ("Verify that the returned list has a single entry. Save the entry as trusted_root_original")
54+
55+
56+
def verify_failsafe_status() -> str:
57+
return ("Verify that the DUT responds with FAILSAFE_REQUIRED")
58+
59+
60+
def verify_armfailsafe_response() -> str:
61+
return ("Verify that DUT sends ArmFailSafeResponse with the ErrorCode set to OK")
62+
63+
64+
def verify_noc_response(status) -> str:
65+
return (f"Verify that the DUT responds with a NOCResponse with the StatusCode field set to {status}")
66+
67+
68+
def verify_csr_not_update() -> str:
69+
return ("Verify that the DUT returns a CSRResponse and save as csr_not_update")
70+
71+
72+
def verify_constraint_error() -> str:
73+
return ("Verify that the DUT responds with CONSTRAINT_ERROR")
74+
75+
76+
def verify_csr_pase() -> str:
77+
return ("Verify that the DUT returns a CSRResponse and save as csr_pase")
78+
79+
80+
def verify_unsupported_access() -> str:
81+
return ("Verify that the DUT responds with UNSUPPORTED_ACCESS")
82+
83+
84+
class TC_OPCREDS_3_4(MatterBaseTest):
85+
def desc_TC_OPCREDS_3_4(self):
86+
return " UpdateNOC-Error Condition [DUT-Server]"
87+
88+
def pics_OPCREDS_3_4(self):
89+
return ['OPCREDS.S']
90+
91+
def steps_TC_OPCREDS_3_4(self):
92+
return [TestStep(1, commission_if_required('TH1'), is_commissioning=True),
93+
TestStep(
94+
2, f"TH1 {read_attribute('NOCs')} from the Node Operational Credentials cluster using a fabric-filtered read. Save the NOCs as nocs.", None, verify_noc()),
95+
TestStep(
96+
3, f"TH1 {read_attribute('TrustedRootCertificates')} attribute from the Node Operational Credentials cluster", None, verify_trusted_root_original()),
97+
TestStep(
98+
4, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster with the following fields: NOCValue and ICACValue", None, verify_failsafe_status()),
99+
TestStep(
100+
5, f"TH1 {send_command('ArmFailSafe')} to the DUT with the ExpiryLengthSeconds field set to 900", None, verify_armfailsafe_response()),
101+
TestStep(
102+
6, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster with the following fields: NOCValue and ICACValue", None, verify_noc_response("MissingCsr")),
103+
TestStep(
104+
7, f"TH1 {send_command('CSRRequest')} with the IsForUpdateNOC field set to false", None, verify_csr_not_update()),
105+
TestStep(8, "TH1 generates a new NOC chain with ICAC with the following properties"),
106+
TestStep(
107+
9, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster", None, verify_constraint_error()),
108+
TestStep(
109+
10, f"TH1 {send_command('CSRequest')} with the IsForUpdateNOC field set to true", None, verify_csr_not_update()),
110+
TestStep(
111+
11, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster", None, verify_noc_response("InvalidPublicKey")),
112+
TestStep(12, "TH1 generates a new Trusted Root Certificate and Private Key and saves as new_root_cert and new_root_key so that TH can generate an NOC for UpdateNOC that doesn’t chain to the original root"),
113+
TestStep(13, "TH1 generates a new NOC and ICAC"),
114+
TestStep(
115+
14, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster", None, verify_noc_response("InvalidNOC")),
116+
TestStep(15, "TH1 generates a new NOC and ICAC"),
117+
TestStep(
118+
16, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster", None, verify_noc_response("InvalidNOC")),
119+
TestStep(17, "TH1 generates a new NOC and ICAC"),
120+
TestStep(
121+
18, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster", None, verify_noc_response("InvalidNOC")),
122+
TestStep(
123+
19, f"TH1 {send_command('AddTrustedRootCertificate')} to DUT again with the RootCACertificate field set to new_root_cert"),
124+
TestStep(
125+
20, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster", None, verify_constraint_error()),
126+
TestStep(
127+
21, f"TH1 {send_command('ArmFailSafe')} to the DUT with the ExpiryLengthSeconds field set to 0", None, verify_armfailsafe_response()),
128+
TestStep(22, f"TH1 {send_command('OpenCommissioningWindow')} to the DUT"),
129+
TestStep(
130+
23, f"TH1 connects to the DUT over PASE and {send_command('ArmFailSafe')} to the DUT with the ExpiryLengthSeconds field set to 900. Steps 24-26 are all performed over the PASE connection.", None, verify_armfailsafe_response()),
131+
TestStep(
132+
24, f"TH1 {send_command('CSRequest')} over PASE with the IsForUpdateNOC field set to true", None, verify_csr_pase()),
133+
TestStep(25, "TH1 generates a new NOC chain with ICAC with the following properties: new NOC and ICAC using icac_pase"),
134+
TestStep(26, f"TH1 {send_command('UpdateNOC')} to the Node Operational Credentials cluster over PASE", None, verify_unsupported_access())]
135+
136+
@async_test_body
137+
async def test_TC_OPCREDS_3_4(self):
138+
self.step(1)
139+
opcreds = Clusters.OperationalCredentials
140+
141+
self.step(2)
142+
nocs = await self.read_single_attribute_check_success(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cluster=opcreds, attribute=opcreds.Attributes.NOCs, fabric_filtered=True)
143+
if nocs[0].noc:
144+
noc_original = nocs[0].noc
145+
else:
146+
asserts.assert_true(False, "Unexpected fail reading NOC Value on NOCs response")
147+
148+
if nocs[0].icac:
149+
icac_original = nocs[0].icac
150+
else:
151+
asserts.fail("Unexpected fail reading ICAC Value on NOCs response")
152+
153+
self.step(3)
154+
trusted_root_list_original = await self.read_single_attribute_check_success(
155+
dev_ctrl=self.default_controller,
156+
node_id=self.dut_node_id, cluster=opcreds,
157+
attribute=opcreds.Attributes.TrustedRootCertificates)
158+
asserts.assert_equal(len(trusted_root_list_original), 1,
159+
"Unexpected number of entries in the TrustedRootCertificates table")
160+
161+
self.step(4)
162+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_original, ICACValue=icac_original)
163+
try:
164+
await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
165+
asserts.fail("Unexpected error sending UpdateNOC command")
166+
except InteractionModelError as e:
167+
asserts.assert_equal(e.status, Status.FailsafeRequired, "Unexpected Failsafe status")
168+
169+
self.step(5)
170+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(expiryLengthSeconds=900)
171+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
172+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
173+
"Failure status returned from arm failsafe")
174+
175+
self.step(6)
176+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_original, ICACValue=icac_original)
177+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
178+
asserts.assert_equal(resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kMissingCsr,
179+
"Failure status returned from UpdateNOC")
180+
181+
self.step(7)
182+
cmd = opcreds.Commands.CSRRequest(CSRNonce=random.randbytes(32), isForUpdateNOC=False)
183+
csr_not_update = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
184+
185+
self.step(8)
186+
new_noc_chain = await self.default_controller.IssueNOCChain(csr_not_update, self.dut_node_id)
187+
noc_not_for_update = new_noc_chain.nocBytes
188+
icac_not_for_update = new_noc_chain.icacBytes
189+
190+
self.step(9)
191+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_not_for_update, ICACValue=icac_not_for_update)
192+
try:
193+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
194+
asserts.fail("Unexpected error sending UpdateNOC command")
195+
except InteractionModelError as e:
196+
asserts.assert_equal(e.status, Status.ConstraintError, "Failure status returned from UpdateNOC")
197+
198+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(0)
199+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
200+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
201+
"Failure status returned from arm failsafe")
202+
203+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(900)
204+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
205+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
206+
"Failure status returned from arm failsafe")
207+
208+
self.step(10)
209+
cmd = opcreds.Commands.CSRRequest(CSRNonce=random.randbytes(32), isForUpdateNOC=True)
210+
csr_update = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
211+
212+
self.step(11)
213+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_original, ICACValue=icac_original)
214+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
215+
asserts.assert_equal(resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kInvalidPublicKey,
216+
"Unexpected error code on AddNOC with mismatched CSR")
217+
218+
self.step(12)
219+
th1_ca_new = self.certificate_authority_manager.NewCertificateAuthority()
220+
th1_fabric_admin_new = th1_ca_new.NewFabricAdmin(vendorId=0xFFF2, fabricId=self.matter_test_config.fabric_id)
221+
th1_new = th1_fabric_admin_new.NewController(nodeId=self.default_controller.nodeId+1)
222+
223+
self.step(13)
224+
th1_certs_new = await th1_new.IssueNOCChain(csr_update, self.dut_node_id+1)
225+
new_root_cert = th1_certs_new.rcacBytes
226+
noc_update_new_root = th1_certs_new.nocBytes
227+
icac_update_new_root = th1_certs_new.icacBytes
228+
229+
self.step(14)
230+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_update_new_root, ICACValue=icac_update_new_root)
231+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
232+
asserts.assert_equal(resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kInvalidNOC,
233+
"NOCResponse with the StatusCode InvalidNOC")
234+
235+
self.step(15)
236+
# new NOC is generated from the NOCSR returned in csr_update with the matter-fabric-id set to a different
237+
# value than noc_original. The NOC is signed by new ICA. Save as noc_update_bad_fabric_on_noc.
238+
noc_update_bad_fabric_on_noc = th1_certs_new.nocBytes
239+
# new ICAC is generated with the and matter-fabric-id omitted. ICAC is signed by the original key for
240+
# trusted_root_original. Save as icac_update_bad_fabric_on_noc
241+
icac_update_bad_fabric_on_noc = new_noc_chain.icacBytes
242+
243+
self.step(16)
244+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_update_bad_fabric_on_noc, ICACValue=icac_update_bad_fabric_on_noc)
245+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
246+
asserts.assert_equal(resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kInvalidNOC,
247+
"NOCResponse with the StatusCode InvalidNOC")
248+
249+
self.step(17)
250+
noc_update_bad_fabric_on_icac = th1_certs_new.nocBytes
251+
icac_update_bad_fabric_on_icac = new_noc_chain.icacBytes
252+
253+
self.step(18)
254+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_update_bad_fabric_on_icac, ICACValue=icac_update_bad_fabric_on_icac)
255+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
256+
asserts.assert_equal(resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kInvalidNOC,
257+
"NOCResponse with the StatusCode InvalidNOC")
258+
259+
self.step(19)
260+
cmd = opcreds.Commands.AddTrustedRootCertificate(new_root_cert)
261+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
262+
263+
self.step(20)
264+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_update_new_root, ICACValue=icac_update_new_root)
265+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
266+
asserts.assert_equal(resp.statusCode, opcreds.Enums.NodeOperationalCertStatusEnum.kMissingCsr,
267+
"Failure status returned from UpdateNOC")
268+
269+
self.step(21)
270+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(0)
271+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
272+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
273+
"Failure status returned from arm failsafe")
274+
275+
self.step(22)
276+
resp = await self.openCommissioningWindow(self.default_controller, self.dut_node_id)
277+
278+
self.step(23)
279+
await self.default_controller.FindOrEstablishPASESession(setupCode=resp.commissioningParameters.setupQRCode, nodeid=self.dut_node_id)
280+
281+
cmd = Clusters.GeneralCommissioning.Commands.ArmFailSafe(900)
282+
resp = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
283+
asserts.assert_equal(resp.errorCode, Clusters.GeneralCommissioning.Enums.CommissioningErrorEnum.kOk,
284+
"Error code status returned from arm failsafe")
285+
286+
self.step(24)
287+
cmd = opcreds.Commands.CSRRequest(CSRNonce=random.randbytes(32))
288+
csr_pase = await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
289+
290+
self.step(25)
291+
new_noc_chain = await self.default_controller.IssueNOCChain(csr_pase, self.dut_node_id)
292+
noc_pase = csr_pase.NOCSRElements
293+
icac_pase = new_noc_chain.icacBytes
294+
295+
self.step(26)
296+
cmd = opcreds.Commands.UpdateNOC(NOCValue=noc_pase, ICACValue=icac_pase)
297+
try:
298+
await self.send_single_cmd(dev_ctrl=self.default_controller, node_id=self.dut_node_id, cmd=cmd)
299+
asserts.fail("Unexpected error sending UpdateNOC command")
300+
except InteractionModelError as e:
301+
asserts.assert_equal(e.status, Status.UnsupportedAccess, "Failure status returned from UpdateNOC")
302+
303+
304+
if __name__ == "__main__":
305+
default_matter_test_main()

src/python_testing/test_plan_support.py

+8
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ def read_attribute(attribute: str, cluster: typing.Optional[str] = None):
2525
return attr
2626

2727

28+
def send_command(command: str, cluster: typing.Optional[str] = None):
29+
cmd = f"sends the {command} command"
30+
if cluster:
31+
return f'{cmd} from {cluster}'
32+
else:
33+
return cmd
34+
35+
2836
def save_as(val: str) -> str:
2937
return f' and saves the value as {val}'
3038

0 commit comments

Comments
 (0)