[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