From dc133baa68d8adb2865cda6a115fee25b23839e2 Mon Sep 17 00:00:00 2001 From: Jing Ma Date: Thu, 27 Aug 2020 12:18:07 +0800 Subject: [PATCH] [thread-cert] refactor case 8.1.2, 8.2.1 and 8.2.2 using pktverify (#5411) --- .../thread-cert/Cert_8_1_02_Commissioning.py | 56 +++++++++++++++++-- .../thread-cert/Cert_8_2_01_JoinerRouter.py | 41 +++++++++++++- .../thread-cert/Cert_8_2_02_JoinerRouter.py | 45 +++++++++++++-- tests/scripts/thread-cert/pktverify/consts.py | 37 ++++++++++++ .../thread-cert/pktverify/layer_fields.py | 6 ++ 5 files changed, 173 insertions(+), 12 deletions(-) diff --git a/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py b/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py index 7f2eddc01..460fc214f 100755 --- a/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py +++ b/tests/scripts/thread-cert/Cert_8_1_02_Commissioning.py @@ -30,6 +30,8 @@ import unittest import thread_cert +from pktverify.consts import MLE_DISCOVERY_RESPONSE, HANDSHAKE_CLIENT_HELLO, HANDSHAKE_SERVER_HELLO, HANDSHAKE_SERVER_KEY_EXCHANGE, HANDSHAKE_SERVER_HELLO_DONE, HANDSHAKE_CLIENT_KEY_EXCHANGE, HANDSHAKE_HELLO_VERIFY_REQUEST, NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV, CONTENT_CHANGE_CIPHER_SPEC, CONTENT_ALERT, CONTENT_HANDSHAKE +from pktverify.packet_verifier import PacketVerifier COMMISSIONER = 1 JOINER = 2 @@ -40,12 +42,14 @@ class Cert_8_1_02_Commissioning(thread_cert.TestCase): TOPOLOGY = { COMMISSIONER: { - 'masterkey': 'deadbeefdeadbeefdeadbeefdeadbeef', + 'name': 'COMMISSIONER', + 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'panid': 0xface }, JOINER: { - 'masterkey': '00112233445566778899aabbccddeeff', + 'name': 'JOINER', + 'masterkey': 'deadbeefdeadbeefdeadbeefdeadbeef', 'mode': 'rsdn', 'router_selection_jitter': 1 }, @@ -63,10 +67,50 @@ class Cert_8_1_02_Commissioning(thread_cert.TestCase): self.nodes[JOINER].interface_up() self.nodes[JOINER].joiner_start('10DKSP') self.simulator.go(10) - self.assertNotEqual( - self.nodes[JOINER].get_masterkey(), - self.nodes[COMMISSIONER].get_masterkey(), - ) + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + COMMISSIONER = pv.vars['COMMISSIONER'] + _cpkts = pkts.filter_wpan_src64(COMMISSIONER) + _cpkts2 = pkts.filter_wpan_dst64(COMMISSIONER) + + # Step 3: Verify the following details occur in the exchange between + # Joiner_1 and the Commissioner + # 1. UDP port (Specified by the Commissioner: in Discovery Response) is used as destination port for UDP datagrams from Joiner_1 to the Commissioner. + _cpkts.filter_mle_cmd(MLE_DISCOVERY_RESPONSE).must_next().must_verify( + lambda p: { + NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, + NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV + } == set(p.thread_meshcop.tlv.type)) + + # 2. Joiner_1 sends an initial DTLS-ClientHello handshake record to the Commissioner + _cpkts2.range(_cpkts.index).filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next() + + # 3. The Commissioner must correctly receive the initial DTLS-ClientHello handshake record and send a DTLS-HelloVerifyRequest handshake record to Joiner_1 + _pkt = _cpkts.range( + _cpkts2.index).filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_HELLO_VERIFY_REQUEST]).must_next() + _pkt.must_verify(lambda p: p.dtls.handshake.cookie is not None) + + # 4. Joiner_1 receives the DTLS-HelloVerifyRequest handshake record and sends a subsequent DTLS-ClientHello handshake record in one UDP datagram to the Commissioner + # 5. Verify that both DTLS-HelloVerifyRequest and subsequent DTLS-ClientHello contain the same cookie + _cpkts2.filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next().must_verify( + lambda p: p.dtls.handshake.cookie == _pkt.dtls.handshake.cookie) + + # 6. The Commissioner must correctly receive the subsequent DTLSClientHello handshake record and then send, in order, DTLSServerHello, DTLS-ServerKeyExchange and DTLSServerHelloDone handshake records to Joiner_1 + _cpkts.filter( + lambda p: p.dtls.handshake.type == + [HANDSHAKE_SERVER_HELLO, HANDSHAKE_SERVER_KEY_EXCHANGE, HANDSHAKE_SERVER_HELLO_DONE]).must_next() + + # 7. Joiner_1 receives the DTLS-ServerHello, DTLSServerKeyExchange and DTLS-ServerHelloDone handshake records and sends, in order, a DTLS-ClientKeyExchange handshake record, a DTLS-ChangeCipherSpec record and an encrypted DTLS-Finished handshake record to the Commissioner. + _cpkts2.filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_KEY_EXCHANGE]).must_next().must_verify( + lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_KEY_EXCHANGE] and set( + p.dtls.record.content_type) == {CONTENT_CHANGE_CIPHER_SPEC, CONTENT_HANDSHAKE}) + + # 8. The Commissioner must receive the DTLS-ClientKeyExchange handshake record, the DTLS-ChangeCipherSpec and the encrypted DTLS-Finished handshake record, and then send a DTLS-Alert record with error code 20 (bad record MAC) - in one UDP datagram - to Joiner_1 + _cpkts.filter( + lambda p: p.dtls.record.content_type == [CONTENT_ALERT] and p.dtls.alert_message.desc == 20).must_next() if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py b/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py index 43f6d47b7..ab7d83a05 100755 --- a/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py +++ b/tests/scripts/thread-cert/Cert_8_2_01_JoinerRouter.py @@ -30,6 +30,8 @@ import unittest import thread_cert +from pktverify.consts import MLE_CHILD_ID_RESPONSE, MLE_DISCOVERY_RESPONSE, HANDSHAKE_CLIENT_HELLO, NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV, RLY_RX_URI, RLY_TX_URI +from pktverify.packet_verifier import PacketVerifier COMMISSIONER = 1 JOINER_ROUTER = 2 @@ -41,17 +43,20 @@ class Cert_8_2_01_JoinerRouter(thread_cert.TestCase): TOPOLOGY = { COMMISSIONER: { - 'masterkey': 'deadbeefdeadbeefdeadbeefdeadbeef', + 'name': 'COMMISSIONER', + 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'panid': 0xface, 'router_selection_jitter': 1 }, JOINER_ROUTER: { + 'name': 'JOINER_ROUTER', 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'router_selection_jitter': 1 }, JOINER: { + 'name': 'JOINER', 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'router_selection_jitter': 1 @@ -99,6 +104,40 @@ class Cert_8_2_01_JoinerRouter(thread_cert.TestCase): self.nodes[JOINER].thread_start() self.simulator.go(5) self.assertEqual(self.nodes[JOINER].get_state(), 'router') + self.collect_rloc16s() + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + COMMISSIONER = pv.vars['COMMISSIONER'] + _cpkts = pkts.filter_wpan_src64(COMMISSIONER) + _cpkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next() + + # Step 3: Verify that the following details occur in the exchange between the Joiner, + # the Joiner_Router and the Commissioner + # 1. UDP port (Specified by the Commissioner: in Discovery Response) is used as destination port + # for UDP datagrams from Joiner_1 to the Commissioner. + pkts.range(_cpkts.index).filter_mle_cmd(MLE_DISCOVERY_RESPONSE).must_next().must_verify( + lambda p: { + NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, + NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV + } == set(p.thread_meshcop.tlv.type)) + + # 2. Joiner_1 sends an initial DTLS-ClientHello handshake record to the Commissioner + pkts.filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next() + + # 3. The Joiner_Router receives the initial DTLS-ClientHello handshake record and sends a RLY_RX.ntf + # message to the Commissioner + # Todo: verify coap payload + jr_rloc16 = pv.vars["JOINER_ROUTER_RLOC16"] + c_rloc16 = pv.vars["COMMISSIONER_RLOC16"] + pkts.filter_coap_request(RLY_RX_URI).must_next().must_verify( + lambda p: p.wpan.src16 == jr_rloc16 and p.wpan.dst16 == c_rloc16) + + # 4. The Commissioner receives the RLY_RX.ntf message and sends a RLY_TX.ntf message to the Joiner_Router + pkts.filter_coap_request(RLY_TX_URI).must_next().must_verify( + lambda p: p.wpan.src16 == c_rloc16 and p.wpan.dst16 == jr_rloc16) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py b/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py index c12fa31cc..6951e9f40 100755 --- a/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py +++ b/tests/scripts/thread-cert/Cert_8_2_02_JoinerRouter.py @@ -30,6 +30,8 @@ import unittest import thread_cert +from pktverify.consts import MLE_CHILD_ID_RESPONSE, MLE_DISCOVERY_RESPONSE, HANDSHAKE_CLIENT_HELLO, NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV, RLY_RX_URI, RLY_TX_URI +from pktverify.packet_verifier import PacketVerifier COMMISSIONER = 1 JOINER_ROUTER = 2 @@ -41,17 +43,20 @@ class Cert_8_2_02_JoinerRouter(thread_cert.TestCase): TOPOLOGY = { COMMISSIONER: { - 'masterkey': 'deadbeefdeadbeefdeadbeefdeadbeef', + 'name': 'COMMISSIONER', + 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'panid': 0xface, 'router_selection_jitter': 1 }, JOINER_ROUTER: { + 'name': 'JOINER_ROUTER', 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'router_selection_jitter': 1 }, JOINER: { + 'name': 'JOINER', 'masterkey': '00112233445566778899aabbccddeeff', 'mode': 'rsdn', 'router_selection_jitter': 1 @@ -91,10 +96,40 @@ class Cert_8_2_02_JoinerRouter(thread_cert.TestCase): self.nodes[JOINER].interface_up() self.nodes[JOINER].joiner_start('20DKSP') self.simulator.go(10) - self.assertNotEqual( - self.nodes[JOINER].get_masterkey(), - self.nodes[COMMISSIONER].get_masterkey(), - ) + self.collect_rloc16s() + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + COMMISSIONER = pv.vars['COMMISSIONER'] + _cpkts = pkts.filter_wpan_src64(COMMISSIONER) + _cpkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next() + + # Step 3: Verify that the following details occur in the exchange between the Joiner, + # the Joiner_Router and the Commissioner + # 1. UDP port (Specified by the Commissioner: in Discovery Response) is used as destination port + # for UDP datagrams from Joiner_1 to the Commissioner. + pkts.range(_cpkts.index).filter_mle_cmd(MLE_DISCOVERY_RESPONSE).must_next().must_verify( + lambda p: { + NM_EXTENDED_PAN_ID_TLV, NM_NETWORK_NAME_TLV, NM_STEERING_DATA_TLV, NM_COMMISSIONER_UDP_PORT_TLV, + NM_JOINER_UDP_PORT_TLV, NM_DISCOVERY_RESPONSE_TLV + } == set(p.thread_meshcop.tlv.type)) + + # 2. Joiner_1 sends an initial DTLS-ClientHello handshake record to the Commissioner + pkts.filter(lambda p: p.dtls.handshake.type == [HANDSHAKE_CLIENT_HELLO]).must_next() + + # 3. The Joiner_Router receives the initial DTLS-ClientHello handshake record and sends a RLY_RX.ntf + # message to the Commissioner + # Todo: verify coap payload + jr_rloc16 = pv.vars["JOINER_ROUTER_RLOC16"] + c_rloc16 = pv.vars["COMMISSIONER_RLOC16"] + pkts.filter_coap_request(RLY_RX_URI).must_next().must_verify( + lambda p: p.wpan.src16 == jr_rloc16 and p.wpan.dst16 == c_rloc16) + + # 4. The Commissioner receives the RLY_RX.ntf message and sends a RLY_TX.ntf message to the Joiner_Router + pkts.filter_coap_request(RLY_TX_URI).must_next().must_verify( + lambda p: p.wpan.src16 == c_rloc16 and p.wpan.dst16 == jr_rloc16) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/pktverify/consts.py b/tests/scripts/thread-cert/pktverify/consts.py index a6808491d..38620a74d 100644 --- a/tests/scripts/thread-cert/pktverify/consts.py +++ b/tests/scripts/thread-cert/pktverify/consts.py @@ -73,6 +73,8 @@ MLE_CHILD_ID_RESPONSE = 12 MLE_CHILD_UPDATE_REQUEST = 13 MLE_CHILD_UPDATE_RESPONSE = 14 MLE_ANNOUNCE = 15 +MLE_DISCOVERY_REQUEST = 16 +MLE_DISCOVERY_RESPONSE = 17 # COAP URIs ADDR_QRY_URI = '/a/aq' @@ -82,6 +84,8 @@ ADDR_SOL_URI = '/a/as' ADDR_REL_URI = '/a/ar' SVR_DATA_URI = '/a/sd' ND_DATA_URI = '/a/nd' +RLY_RX_URI = '/c/rx' +RLY_TX_URI = '/c/tx' # MLE TLVs SOURCE_ADDRESS_TLV = 0 @@ -124,6 +128,38 @@ NL_ND_OPTION_TLV = 8 NL_ND_DATA_TLV = 9 NL_THREAD_NETWORK_DATA_TLV = 10 +# MeshCop TLVs +NM_CHANNEL_TLV = 0 +NM_PAN_ID_TLV = 1 +NM_EXTENDED_PAN_ID_TLV = 2 +NM_NETWORK_NAME_TLV = 3 +NM_PSKC_TLV = 4 +NM_NETWORK_MASTER_KEY_TLV = 5 +NM_NETWORK_KEY_SEQUENCE_COUNTER_TLV = 6 +NM_NETWORK_MESH_LOCAL_PREFIX_TLV = 7 +NM_STEERING_DATA_TLV = 8 +NM_SECURITY_POLICY_TLV = 12 +NM_COMMISSIONER_UDP_PORT_TLV = 15 +NM_JOINER_UDP_PORT_TLV = 18 +NM_CHANNEL_MASK_TLV = 53 +NM_DISCOVERY_RESPONSE_TLV = 129 + +# DTLS +HANDSHAKE_HELLO_REQUEST = 0 +HANDSHAKE_CLIENT_HELLO = 1 +HANDSHAKE_SERVER_HELLO = 2 +HANDSHAKE_HELLO_VERIFY_REQUEST = 3 +HANDSHAKE_CERTIFICATE = 11 +HANDSHAKE_SERVER_KEY_EXCHANGE = 12 +HANDSHAKE_CERTIFICATE_REQUEST = 13 +HANDSHAKE_SERVER_HELLO_DONE = 14 +HANDSHAKE_CERTIFICATE_VERIFY = 15 +HANDSHAKE_CLIENT_KEY_EXCHANGE = 16 +HANDSHAKE_FINISHED = 20 +CONTENT_CHANGE_CIPHER_SPEC = 20 +CONTENT_ALERT = 21 +CONTENT_HANDSHAKE = 22 + # DUA related constants ADDRESS_QUERY_INITIAL_RETRY_DELAY = 15 @@ -148,6 +184,7 @@ INFINITE_COST_TIMEOUT = 90 REAL_LAYER_NAMES = { 'mle', 'coap', + 'dtls', 'wpan', 'eth', 'tcp', diff --git a/tests/scripts/thread-cert/pktverify/layer_fields.py b/tests/scripts/thread-cert/pktverify/layer_fields.py index ccc307e8a..66a15eddc 100644 --- a/tests/scripts/thread-cert/pktverify/layer_fields.py +++ b/tests/scripts/thread-cert/pktverify/layer_fields.py @@ -479,6 +479,12 @@ _LAYER_FIELDS = { 'coap.tlv.router_mask_assigned': _auto, 'coap.tlv.router_mask_id_seq': _auto, + # dtls + 'dtls.handshake.type': _list(_auto), + 'dtls.handshake.cookie': _auto, + 'dtls.record.content_type': _list(_auto), + 'dtls.alert_message.desc': _auto, + # thread_address 'thread_address.tlv.type': _list(_auto), 'thread_address.tlv.status': _auto,