[RFC PATCH 33/34] tests: Proximity Ranging test cases

Peddolla Harshavardhan Reddy peddolla at qti.qualcomm.com
Thu May 15 00:17:56 PDT 2025


Signed-off-by: Peddolla Harshavardhan Reddy <peddolla at qti.qualcomm.com>
---
 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




More information about the Hostap mailing list