Signed-off-by: Peddolla Harshavardhan Reddy <peddolla@xxxxxxxxxxxxxxxx> --- tests/hwsim/test_pr.py | 246 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 tests/hwsim/test_pr.py diff --git a/tests/hwsim/test_pr.py b/tests/hwsim/test_pr.py new file mode 100644 index 000000000..29175ee01 --- /dev/null +++ b/tests/hwsim/test_pr.py @@ -0,0 +1,246 @@ +# Test cases for Proximity Ranging features like unsynchronized service +# discovery (PR USD), PASN Authentication for Ranging negotiation and security. +# Copyright (c) 2025, Qualcomm Innovation Center, Inc. +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import binascii +import logging + +from hwsim import HWSimRadio + +logger = logging.getLogger() +import os +import hwsim_utils + +from wpasupplicant import WpaSupplicant +from test_nan_usd import check_nan_usd_capab +from test_pasn import check_pasn_capab + +def split_pr_nan_event(ev): + vals = dict() + for p in ev.split(' ')[1:]: + name, val = p.split('=') + vals[name] = val + return vals + +def check_pr_capab(dev): + check_nan_usd_capab(dev) + check_pasn_capab(dev) + +def test_pr_usd_publish_invalid_param(dev): + """PR USD Publish with invalid parameters""" + check_pr_capab(dev[0]) + + # Both solicited and unsolicited disabled is invalid + cmd = "NAN_PUBLISH service_name=_test solicited=0 unsolicited=0 pr=1" + id0 = dev[0].global_request(cmd) + if "FAIL" not in id0: + raise Exception("NAN_PUBLISH accepts both solicited=0 and unsolicited=0 with pr=1") + +def test_pr_usd_publish(dev, apdev): + """PR USD Publish""" + check_pr_capab(dev[0]) + cmd = "NAN_PUBLISH unsolicited=0 srv_proto_type=2 ssi=6677 pr=1" + id0 = dev[0].global_request(cmd) + if "FAIL" in id0: + raise Exception("NAN_PUBLISH for PR failed") + + cmd = "NAN_UPDATE_PUBLISH publish_id=" + id0 + " ssi=1122334455" + if "FAIL" in dev[0].global_request(cmd): + raise Exception("NAN_UPDATE_PUBLISH for PR failed") + + cmd = "NAN_CANCEL_PUBLISH publish_id=" + id0 + if "FAIL" in dev[0].global_request(cmd): + raise Exception("NAN_CANCEL_PUBLISH for PR failed") + + ev = dev[0].wait_global_event(["NAN-PUBLISH-TERMINATED"], timeout=1) + if ev is None: + raise Exception("PublishTerminated event not seen") + if "publish_id=" + id0 not in ev: + raise Exception("Unexpected publish_id: " + ev) + if "reason=user-request" not in ev: + raise Exception("Unexpected reason: " + ev) + + cmd = "NAN_PUBLISH pr=1" + count = 0 + for i in range(256): + if "FAIL" in dev[0].global_request(cmd): + break + count += 1 + logger.info("Maximum services: %d" % count) + for i in range(count): + cmd = "NAN_CANCEL_PUBLISH publish_id=%s" % (i + 1) + if "FAIL" in dev[0].global_request(cmd): + raise Exception("NAN_CANCEL_PUBLISH failed") + + ev = dev[0].wait_global_event(["NAN-PUBLISH-TERMINATED"], timeout=1) + if ev is None: + raise Exception("PublishTerminated event not seen") + +def test_pr_usd_subscribe(dev, apdev): + """PR USD Subscribe""" + check_pr_capab(dev[0]) + cmd = "NAN_SUBSCRIBE active=1 srv_proto_type=2 ssi=1122334455 pr=1" + id0 = dev[0].global_request(cmd) + if "FAIL" in id0: + raise Exception("NAN_SUBSCRIBE for PR failed") + + cmd = "NAN_CANCEL_SUBSCRIBE subscribe_id=" + id0 + if "FAIL" in dev[0].global_request(cmd): + raise Exception("NAN_CANCEL_SUBSCRIBE for PR failed") + + ev = dev[0].wait_global_event(["NAN-SUBSCRIBE-TERMINATED"], timeout=1) + if ev is None: + raise Exception("SubscribeTerminated event not seen") + if "subscribe_id=" + id0 not in ev: + raise Exception("Unexpected subscribe_id: " + ev) + if "reason=user-request" not in ev: + raise Exception("Unexpected reason: " + ev) + +def test_pr_usd_match(dev, apdev): + """PR USD Publish/Subscribe match""" + check_pr_capab(dev[0]) + check_pr_capab(dev[1]) + cmd = "NAN_SUBSCRIBE active=1 srv_proto_type=2 ssi=1122334455 pr=1" + id0 = dev[0].global_request(cmd) + if "FAIL" in id0: + raise Exception("NAN_SUBSCRIBE for PR failed") + + cmd = "NAN_PUBLISH unsolicited=0 srv_proto_type=2 ssi=6677 ttl=5 pr=1" + id1 = dev[1].global_request(cmd) + if "FAIL" in id1: + raise Exception("NAN_PUBLISH for PR failed") + + ev = dev[0].wait_global_event(["NAN-DISCOVERY-RESULT"], timeout=5) + if ev is None: + raise Exception("DiscoveryResult event not seen") + if "srv_proto_type=2" not in ev.split(' '): + raise Exception("Unexpected srv_proto_type: " + ev) + if "ssi=6677" not in ev.split(' '): + raise Exception("Unexpected ssi: " + ev) + + dev[0].global_request("NAN_CANCEL_SUBSCRIBE subscribe_id=" + id0) + dev[1].global_request("NAN_CANCEL_PUBLISH publish_id=" + id1) + + + +def clear_all_identities(dev): + cmd = "PR_CLEAR_DIK_CONTEXT" + dev[0].request(cmd) + + cmd = "PR_CLEAR_DIK_CONTEXT" + dev[1].request(cmd) + + +def run_pr_secure_negotiation(dev, auth_mode=0): + check_pr_capab(dev[0]) + check_pr_capab(dev[1]) + + cmd = "NAN_SUBSCRIBE active=1 srv_proto_type=2 ssi=1122334455 ttl=10 pr=1 freq=2437" + id0 = dev[0].global_request(cmd) + if "FAIL" in id0: + raise Exception("NAN_SUBSCRIBE for PR failed") + + cmd = "NAN_PUBLISH unsolicited=0 srv_proto_type=2 ssi=6677 ttl=10 pr=1 freq=2437" + id1 = dev[1].global_request(cmd) + if "FAIL" in id1: + raise Exception("NAN_PUBLISH for PR failed") + + ev = dev[0].wait_global_event(["NAN-DISCOVERY-RESULT"], timeout=5) + if ev is None: + raise Exception("DiscoveryResult event not seen") + if "srv_proto_type=2" not in ev.split(' '): + raise Exception("Unexpected srv_proto_type: " + ev) + if "ssi=6677" not in ev.split(' '): + raise Exception("Unexpected ssi: " + ev) + + ev = dev[1].wait_event(["NAN-REPLIED"], timeout=5) + if ev is None: + raise Exception("Replied event not seen") + vals = split_pr_nan_event(ev) + if vals['publish_id'] != id1: + raise Exception("Unexpected publish_id: " + ev) + if vals['subscribe_id'] != id0: + raise Exception("Unexpected subscribe_id: " + ev) + if vals['address'] != dev[0].own_addr(): + raise Exception("Unexpected address: " + ev) + if vals['srv_proto_type'] != "2": + raise Exception("Unexpected ssi: " + ev) + if vals['ssi'] != "1122334455": + raise Exception("Unexpected ssi: " + ev) + + cmd = "NAN_CANCEL_SUBSCRIBE subscribe_id=" + id0 + if "FAIL" in dev[0].global_request(cmd): + raise Exception("NAN_CANCEL_SUBSCRIBE for PR failed") + cmd = "NAN_CANCEL_PUBLISH publish_id=" + id1 + if "FAIL" in dev[1].global_request(cmd): + raise Exception("NAN_CANCEL_PUBLISH for PR failed") + + peer_addr = dev[1].own_addr() + cmd = f"PR_PASN_START addr={peer_addr} role=ISTA auth={auth_mode} ranging_type=NTB-OPEN-PHY freq=2437" + id0 = dev[0].request(cmd) + if "FAIL" in id0: + raise Exception("PR_PASN_START Failed") + + ev = dev[0].wait_event(["PR-PASN-RESULT"], timeout=5) + if ev is None: + raise Exception("PASN result not seen on subscriber") + + ev = dev[1].wait_global_event(["PR-PASN-RESULT"], timeout=10) + if ev is None: + raise Exception("PASN result not seen on publisher") + +def test_pr_pasn_unauth(dev, apdev): + """PR Secure ranging negotiation with PASN-UNAUTH""" + clear_all_identities(dev) + run_pr_secure_negotiation(dev, auth_mode=0) + +def test_pr_pasn_sae_global_pw(dev, apdev): + """PR Secure ranging negotiation with PASN-SAE with Advertiser global password""" + + clear_all_identities(dev) + cmd = "PR_SET_DIK_CONTEXT self dik=D216C41154D2187E422F1B0F193D0AAD password=asdfghjkl" + dev[1].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT dik=D216C41154D2187E422F1B0F193D0AAD password=asdfghjkl" + dev[0].request(cmd) + + run_pr_secure_negotiation(dev, auth_mode=1) + +def test_pr_pasn_sae_unique_pw(dev, apdev): + """PR Secure ranging negotiation with PASN-SAE with unique password""" + + clear_all_identities(dev) + cmd = "PR_SET_DIK_CONTEXT self dik=D216C41154D2187E422F1B0F193D0BBB" + dev[1].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT self dik=D216C41154D2187E422F1B0F193D0AAA" + dev[0].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT dik=D216C41154D2187E422F1B0F193D0AAA password=qwertyuiop" + dev[1].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT dik=D216C41154D2187E422F1B0F193D0BBB password=qwertyuiop" + dev[0].request(cmd) + + run_pr_secure_negotiation(dev, auth_mode=1) + +def test_pr_pasn_pmk(dev, apdev): + """PR Secure ranging negotiation with PASN-PMK""" + + clear_all_identities(dev) + cmd = "PR_SET_DIK_CONTEXT self dik=D216C41154D2187E422F1B0F193D0111" + dev[1].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT self dik=D216C41154D2187E422F1B0F193D0222" + dev[0].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT dik=D216C41154D2187E422F1B0F193D0222 pmk=7a55426d410b409780d3f2f3e9cb0cf667745c880816ccee5701a0b3bb1ee64d" + dev[1].request(cmd) + + cmd = "PR_SET_DIK_CONTEXT dik=D216C41154D2187E422F1B0F193D0111 pmk=7a55426d410b409780d3f2f3e9cb0cf667745c880816ccee5701a0b3bb1ee64d" + dev[0].request(cmd) + + run_pr_secure_negotiation(dev, auth_mode=2) -- 2.34.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap