{"id":2226439,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2226439/?format=json","web_url":"http://patchwork.ozlabs.org/project/hostap/patch/20260422122424.43776-93-andrei.otcheretianski@intel.com/","project":{"id":22,"url":"http://patchwork.ozlabs.org/api/1.2/projects/22/?format=json","name":"HostAP Development","link_name":"hostap","list_id":"hostap.lists.infradead.org","list_email":"hostap@lists.infradead.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260422122424.43776-93-andrei.otcheretianski@intel.com>","list_archive_url":null,"date":"2026-04-22T12:24:23","name":"[RFC,92/92] tests: Add NAN hwsim pairing tests","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"f20b6591bc0e3b75b6ab0a21658cd5055d50fe8d","submitter":{"id":62065,"url":"http://patchwork.ozlabs.org/api/1.2/people/62065/?format=json","name":"Andrei Otcheretianski","email":"andrei.otcheretianski@intel.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/hostap/patch/20260422122424.43776-93-andrei.otcheretianski@intel.com/mbox/","series":[{"id":501001,"url":"http://patchwork.ozlabs.org/api/1.2/series/501001/?format=json","web_url":"http://patchwork.ozlabs.org/project/hostap/list/?series=501001","date":"2026-04-22T12:23:05","name":"Add NAN PASN pairing support","version":1,"mbox":"http://patchwork.ozlabs.org/series/501001/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2226439/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2226439/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n secure) header.d=lists.infradead.org header.i=@lists.infradead.org\n header.a=rsa-sha256 header.s=bombadil.20210309 header.b=v453zrZv;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256\n header.s=Intel header.b=UXXAhVRS;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=none (no SPF record) smtp.mailfrom=lists.infradead.org\n (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org;\n envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org;\n receiver=patchwork.ozlabs.org)"],"Received":["from bombadil.infradead.org (bombadil.infradead.org\n [IPv6:2607:7c80:54:3::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0zBq1G5Kz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 22:32:35 +1000 (AEST)","from localhost ([::1] helo=bombadil.infradead.org)\n\tby bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1wFWkL-0000000ACKz-2CzU;\n\tWed, 22 Apr 2026 12:32:01 +0000","from mgamail.intel.com ([192.198.163.17])\n\tby bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux))\n\tid 1wFWg2-0000000A34C-3j9q\n\tfor hostap@lists.infradead.org;\n\tWed, 22 Apr 2026 12:27:45 +0000","from orviesa010.jf.intel.com ([10.64.159.150])\n  by fmvoesa111.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 22 Apr 2026 05:27:04 -0700","from iapp347.iil.intel.com (HELO 87c02287900a.iil.intel.com)\n ([10.167.28.6])\n  by orviesa010-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 22 Apr 2026 05:27:03 -0700"],"DKIM-Signature":["v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=lists.infradead.org; s=bombadil.20210309; h=Sender:\n\tContent-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post:\n\tList-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:\n\tMessage-ID:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description:\n\tResent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:\n\tList-Owner; bh=5My1vlCdOgRC1ulFgmATT2JYPTsyql8Sb6BDQ2PbIEs=; b=v453zrZvajLnyT\n\tLALvyLW3VFRz06CNtMDEAHfNs0npiU4RD+pKcVVaFmpOxcEnluLLFjtyWnEnxsnCQmY2JD99EZfUF\n\tbpELozstlDiLUEJ0miej/tHfsh8+3XMSAi95B/W/4oe4tXnBFgFPi/FnKeTj3WWFaCW1ulfHcTaTb\n\tSdyLTjJDVM3ZasEFQ6WyNDC4srN2lmgTU9u63t52Z4KUToHXm9KrEfeWW3vWXi7OvK5QqhVZOjZuY\n\tO3cZQbbp5h2aJSkrFHxRo8ZMPjM+yaquPWhFCkXrg2YQcTyK458safSfIZ4SfZhlgItfgQo0nVF95\n\tFpN4yJmfh/K/YCN8vcBQ==;","v=1; a=rsa-sha256; c=relaxed/simple;\n  d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n  t=1776860854; x=1808396854;\n  h=from:to:cc:subject:date:message-id:in-reply-to:\n   references:mime-version:content-transfer-encoding;\n  bh=vN6x+EYH2MVHQ6ZYP17Fozj4Zz6AjpxgdFFgl6s+EWY=;\n  b=UXXAhVRSRCxqnjACvNrDBGXOAgLbrakFWbk48P9rrM26itpVF0/CjY1d\n   9yw/e1GF+ug7DapUcsv2ym491Gy3ap2KPM7rzehLckW6X6ZEwQxEYKvSU\n   ywNL9Xprs8hJgz4eYZv/RDR8f1aO57VDY5kRDW7dPr8u/eudNlIY5njMM\n   T/PuEoqCMThZ3QN7c73qi8cGqKCkyECaNQsky8aKC7BcAhJwq5GIm3t52\n   DsMOZ8tMfj0/X0D77GmGh2HbQJrsthiOfobeyjGJ8B7dAjqbiAyx5Dtbi\n   fAV2VwKXQGnFKkQqfZOAf6T+kYnOXfXIXJhnc5X0Apzj0NuQOJEKdwA76\n   A==;"],"X-CSE-ConnectionGUID":["R+LXd6QCS/uOHFeMF4OxXQ==","VhLItxV8TOi+Qg4WPicUgg=="],"X-CSE-MsgGUID":["G7nHsSWcQ0yrWqCQOV0BMQ==","NU5K6YInQ1WDsl6ANzQlAw=="],"X-IronPort-AV":["E=McAfee;i=\"6800,10657,11764\"; a=\"77687647\"","E=Sophos;i=\"6.23,192,1770624000\";\n   d=\"scan'208\";a=\"77687647\"","E=Sophos;i=\"6.23,192,1770624000\";\n   d=\"scan'208\";a=\"231445390\""],"X-ExtLoop1":"1","From":"Andrei Otcheretianski <andrei.otcheretianski@intel.com>","To":"hostap@lists.infradead.org","Cc":"vamsin@qti.qualcomm.com,\n\tmaheshkkv@google.com,\n\tAvraham Stern <avraham.stern@intel.com>,\n\tAndrei Otcheretianski <andrei.otcheretianski@intel.com>","Subject":"[RFC 92/92] tests: Add NAN hwsim pairing tests","Date":"Wed, 22 Apr 2026 15:24:23 +0300","Message-ID":"<20260422122424.43776-93-andrei.otcheretianski@intel.com>","X-Mailer":"git-send-email 2.53.0","In-Reply-To":"<20260422122424.43776-1-andrei.otcheretianski@intel.com>","References":"<20260422122424.43776-1-andrei.otcheretianski@intel.com>","MIME-Version":"1.0","X-CRM114-Version":"20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 ","X-CRM114-CacheID":"sfid-20260422_052735_101821_0F2B5620 ","X-CRM114-Status":"GOOD (  13.13  )","X-Spam-Score":"-4.4 (----)","X-Spam-Report":"Spam detection software,\n running on the system \"bombadil.infradead.org\",\n has NOT identified this incoming email as spam.  The original\n message has been attached to this so you can view it or label\n similar future email.  If you have any questions, see\n the administrator of that system for details.\n Content preview:  From: Avraham Stern <avraham.stern@intel.com>\n Signed-off-by:\n    Avraham Stern <avraham.stern@intel.com> Signed-off-by: Andrei\n Otcheretianski\n    <andrei.otcheretianski@intel.com> --- tests/hwsim/test_nan.py | 336\n +++++++++++++++++++++++++++++++++++++++-\n    [...]\n Content analysis details:   (-4.4 points, 5.0 required)\n  pts rule name              description\n ---- ----------------------\n --------------------------------------------------\n -2.3 RCVD_IN_DNSWL_MED      RBL: Sender listed at https://www.dnswl.org/,\n                             medium trust\n                             [192.198.163.17 listed in list.dnswl.org]\n  0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record\n -0.0 SPF_PASS               SPF: sender matches SPF record\n -0.1 DKIM_VALID             Message has at least one valid DKIM or DK\n signature\n -0.1 DKIM_VALID_AU          Message has a valid DKIM or DK signature from\n author's\n                             domain\n -0.1 DKIM_VALID_EF          Message has a valid DKIM or DK signature from\n                             envelope-from domain\n  0.1 DKIM_SIGNED            Message has a DKIM or DK signature,\n not necessarily valid\n -1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%\n                             [score: 0.0000]\n -0.0 DKIMWL_WL_HIGH         DKIMwl.org - High trust sender","X-BeenThere":"hostap@lists.infradead.org","X-Mailman-Version":"2.1.34","Precedence":"list","List-Id":"<hostap.lists.infradead.org>","List-Unsubscribe":"<http://lists.infradead.org/mailman/options/hostap>,\n <mailto:hostap-request@lists.infradead.org?subject=unsubscribe>","List-Archive":"<http://lists.infradead.org/pipermail/hostap/>","List-Post":"<mailto:hostap@lists.infradead.org>","List-Help":"<mailto:hostap-request@lists.infradead.org?subject=help>","List-Subscribe":"<http://lists.infradead.org/mailman/listinfo/hostap>,\n <mailto:hostap-request@lists.infradead.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Sender":"\"Hostap\" <hostap-bounces@lists.infradead.org>","Errors-To":"hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org"},"content":"From: Avraham Stern <avraham.stern@intel.com>\n\nSigned-off-by: Avraham Stern <avraham.stern@intel.com>\nSigned-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com>\n---\n tests/hwsim/test_nan.py | 336 +++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 333 insertions(+), 3 deletions(-)","diff":"diff --git a/tests/hwsim/test_nan.py b/tests/hwsim/test_nan.py\nindex 7278b4d79a..ce7a65fb84 100644\n--- a/tests/hwsim/test_nan.py\n+++ b/tests/hwsim/test_nan.py\n@@ -94,7 +94,7 @@ class NanDevice:\n \n     def publish(self, service_name, ssi=None, unsolicited=1, solicited=1,\n                 sync=1, match_filter_rx=None, match_filter_tx=None,\n-                close_proximity=0, pbm=0):\n+                close_proximity=0, pbm=0, nd_pmk=None, cipher_suites=None):\n \n         cmd = f\"NAN_PUBLISH service_name={service_name} sync={sync} srv_proto_type=2 fsd=0\"\n \n@@ -116,6 +116,12 @@ class NanDevice:\n         if pbm:\n             cmd += f\" pbm={pbm}\"\n \n+        if cipher_suites is not None:\n+            cmd += f\" cipher_suites={cipher_suites}\"\n+\n+        if nd_pmk is not None:\n+            cmd += f\" nd_pmk={nd_pmk}\"\n+\n         return self.wpas.request(cmd)\n \n     def schedule_config(self, *chans, map_id=1):\n@@ -261,11 +267,34 @@ class NanDevice:\n         if \"OK\" not in self.wpas.request(cmd):\n             raise Exception(f\"{self.ifname}: failed to transmit NAN followup\")\n \n+    def pairing_request(self, peer, handle, peer_instance_id, mode, responder=False, password=None):\n+        if mode == \"SAE\":\n+            mode = 1\n+        elif mode == \"PASN\":\n+            mode = 0\n+        elif mode == \"PMK\":\n+            mode = 2\n+\n+        peer_nmi = peer.wpas.own_addr()\n+        cmd = f\"NAN_PAIR {peer_nmi} auth={mode} cipher=GCMP-256 handle={handle} peer_instance_id={peer_instance_id}\"\n+        if password is not None:\n+            cmd += f\" password={password}\"\n+        if responder:\n+            cmd += \" responder\"\n+\n+        if \"OK\" not in self.wpas.request(cmd):\n+            raise Exception(\"NAN_PAIR Failed on requesting device\")\n+\n+    def pair_abort(self, peer_nmi):\n+        cmd = f\"NAN_PAIR_ABORT {peer_nmi}\"\n+        return self.wpas.request(cmd)\n+\n def split_nan_event(ev):\n     vals = dict()\n     for p in ev.split(' ')[1:]:\n-        name, val = p.split('=')\n-        vals[name] = val\n+        if '=' in p:\n+            name, val = p.split('=', 1)\n+            vals[name] = val\n     return vals\n \n def nan_sync_verify_event(ev, addr, pid, sid, ssi):\n@@ -1289,3 +1318,304 @@ def test_nan_bootstrap_password_no_response(dev, apdev, params):\n \n         sub.bootstrap_reset(paddr)\n         sub.cancel_subscribe(sid)\n+\n+def test_nan_pair_abort(dev, apdev, params):\n+    \"\"\"NAN pair abort\"\"\"\n+    with hwsim_nan_radios() as (wpas1, wpas2), \\\n+        NanDevice(wpas1, \"nan0\") as pub, NanDevice(wpas2, \"nan1\") as sub:\n+        pid, sid, paddr, saddr = nan_pre_bootstrap(pub, sub, pmb=0x4)\n+\n+        # Complete bootstrap\n+        sub.bootstrap(paddr, sid, pid, 0x40)\n+        ev = pub.wpas.wait_event([\"NAN-BOOTSTRAP-REQUEST\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-REQUEST event not seen\")\n+        pub.bootstrap(saddr, pid, sid, 0x4, auth=True)\n+        ev = sub.wpas.wait_event([\"NAN-BOOTSTRAP-SUCCESS\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-SUCCESS event not seen (subscriber)\")\n+        ev = pub.wpas.wait_event([\"NAN-BOOTSTRAP-SUCCESS\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-SUCCESS event not seen (publisher)\")\n+\n+        # Start PASN pairing without responder ready\n+        sub.pairing_request(pub, sid, pid, \"SAE\", responder=False, password=\"password123\")\n+\n+        ev_sub = sub.wpas.wait_event([\"NAN-PAIRING-STATUS\"], timeout=2)\n+        if ev_sub is not None:\n+            raise Exception(\"Unexpected PASN result seen on subscriber\")\n+\n+        ev_pub = pub.wpas.wait_event([\"NAN-PAIRING-REQUEST\"], timeout=2)\n+        if ev_pub is None:\n+            raise Exception(\"PASN pairing request not seen on publisher\")\n+\n+        if \"OK\" not in sub.pair_abort(paddr):\n+            raise Exception(\"NAN_PAIR_ABORT failed on subscriber\")\n+        if \"OK\" not in pub.pair_abort(saddr):\n+            raise Exception(\"NAN_PAIR_ABORT failed on publisher\")\n+\n+        # After abort, we should be able to restart pairing successfully\n+        sub.pairing_request(pub, sid, pid, \"SAE\", responder=False, password=\"password123\")\n+        ev_pub = pub.wpas.wait_event([\"NAN-PAIRING-REQUEST\"], timeout=2)\n+        if ev_pub is None:\n+            raise Exception(\"PASN pairing request not seen on publisher after abort\")\n+\n+        pub.pairing_request(sub, sid, pid, \"SAE\", responder=True, password=\"password123\")\n+        ev_sub = sub.wpas.wait_event([\"NAN-PAIRING-STATUS\"], timeout=5)\n+        if ev_sub is None:\n+            raise Exception(\"PASN result not seen on subscriber\")\n+        if \"status=success\" not in ev_sub:\n+            raise Exception(\"NAN PASN pairing failed on subscriber after abort\")\n+        ev_pub = pub.wpas.wait_event([\"NAN-PAIRING-STATUS\"], timeout=5)\n+        if ev_pub is None:\n+            raise Exception(\"PASN result not seen on publisher\")\n+        if \"status=success\" not in ev_pub:\n+            raise Exception(\"NAN PASN pairing failed on publisher after abort\")\n+\n+        # Test abort with invalid peer address\n+        if \"FAIL\" not in sub.pair_abort(\"02:00:00:00:00:00\"):\n+            raise Exception(\"NAN_PAIR_ABORT with invalid peer address succeeded unexpectedly\")\n+\n+def run_nan_pairing(sub, pub, pid, sid, pairing_type, password=None):\n+    if pairing_type == \"SAE\":\n+        if password is not None:\n+            pass\n+        else:\n+            raise Exception(\"Password must be provided for SAE pairing\")\n+\n+    pub.pairing_request(sub, sid, pid, pairing_type, responder=True, password=password)\n+    sub.pairing_request(pub, sid, pid, pairing_type, responder=False, password=password)\n+\n+    ev_sub = sub.wpas.wait_event([\"NAN-PAIRING-STATUS\"], timeout=5)\n+    if ev_sub is None:\n+        raise Exception(\"PASN result not seen on requesting device\")\n+    if \"status=success\" not in ev_sub:\n+        raise Exception(\"NAN PASN pairing failed on requesting device\")\n+\n+    ev_pub = pub.wpas.wait_event([\"NAN-PAIRING-STATUS\"], timeout=5)\n+    if ev_pub is None:\n+        raise Exception(\"PASN result not seen on publisher\")\n+    if \"status=success\" not in ev_pub:\n+        raise Exception(\"NAN PASN pairing failed on publisher\")\n+\n+    # Extract nd_pmk from both events\n+    data_sub = split_nan_event(ev_sub)\n+    data_pub = split_nan_event(ev_pub)\n+\n+    nd_pmk_sub = data_sub.get('nd_pmk')\n+    nd_pmk_pub = data_pub.get('nd_pmk')\n+\n+    if nd_pmk_sub is None:\n+        raise Exception(\"nd_pmk not found in subscriber pairing status event\")\n+    if nd_pmk_pub is None:\n+        raise Exception(\"nd_pmk not found in publisher pairing status event\")\n+\n+    if nd_pmk_sub != nd_pmk_pub:\n+        raise Exception(f\"nd_pmk mismatch: sub={nd_pmk_sub}, pub={nd_pmk_pub}\")\n+\n+    logger.info(f\"NAN pairing successful, nd_pmk={nd_pmk_sub}\")\n+\n+    ev = pub.wpas.wait_event([\"NAN-NIK-RECEIVED\"], timeout=1)\n+    ev = sub.wpas.wait_event([\"NAN-NIK-RECEIVED\"], timeout=1)\n+\n+    return nd_pmk_sub\n+\n+def run_nan_pairing_bootstrap(pairing_type, password=None):\n+    with hwsim_nan_radios(count=2) as [wpas1, wpas2], \\\n+        NanDevice(wpas1, \"nan0\") as pub, NanDevice(wpas2, \"nan1\") as sub:\n+        if \"OK\" not in sub.schedule_config((2437, \"0e000000\")):\n+            raise Exception(\"Failed to configure schedule (sub)\")\n+\n+        if \"OK\" not in pub.schedule_config((2437, \"0e000000\")):\n+            raise Exception(\"Failed to configure schedule (pub)\")\n+\n+        pid, sid, paddr, saddr = nan_pre_bootstrap(pub, sub)\n+        sub.bootstrap(paddr, sid, pid, 0x1)\n+\n+        ev = sub.wpas.wait_event([\"NAN-BOOTSTRAP-SUCCESS\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-SUCCESS event not seen\")\n+\n+        ev = pub.wpas.wait_event([\"NAN-BOOTSTRAP-SUCCESS\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-SUCCESS event not seen\")\n+\n+        run_nan_pairing(sub, pub, pid, sid, pairing_type, password)\n+\n+        pub.cancel_publish(pid)\n+        sub.cancel_subscribe(sid)\n+\n+def test_nan_sae_pairing_bootstrap(dev, apdev, params):\n+    \"\"\"NAN Pairing setup using opportunistic bootstrapping\"\"\"\n+    run_nan_pairing_bootstrap(\"SAE\", password=\"password123\")\n+\n+def run_nan_pairing_verification(pairing_type, password=None):\n+    with hwsim_nan_radios(count=2) as [wpas1, wpas2], \\\n+        NanDevice(wpas1, \"nan0\") as pub, NanDevice(wpas2, \"nan1\") as sub:\n+        paddr = pub.wpas.own_addr()\n+        saddr = sub.wpas.own_addr()\n+\n+        if \"OK\" not in sub.schedule_config((2437, \"0e000000\")):\n+            raise Exception(\"Failed to configure schedule (sub)\")\n+\n+        if \"OK\" not in pub.schedule_config((2437, \"0e000000\")):\n+            raise Exception(\"Failed to configure schedule (pub)\")\n+\n+        pid, sid, paddr, saddr = nan_sync_discovery(pub, sub, \"test_service\",\n+                                                    pssi=\"aabbccdd\",\n+                                                    sssi=\"ddbbccaa\",\n+                                                    unsolicited=0)\n+\n+        if pairing_type == \"SAE\":\n+            if password is not None:\n+                pass\n+            else:\n+                raise Exception(\"Password must be provided for SAE pairing\")\n+\n+        run_nan_pairing(sub, pub, pid, sid, pairing_type, password)\n+\n+        pub.cancel_publish(pid)\n+        sub.cancel_subscribe(sid)\n+\n+        import time\n+        time.sleep(1)\n+        pub.wpas.dump_monitor()\n+        sub.wpas.dump_monitor()\n+\n+        pid, sid, paddr, saddr = nan_sync_discovery(pub, sub, \"test_service\",\n+                                                    pssi=\"aabbccee\",\n+                                                    sssi=\"ddbbccee\",\n+                                                    unsolicited=0,\n+                                                    timeout=5)\n+\n+        run_nan_pairing(sub, pub, pid, sid, \"PMK\")\n+\n+def test_nan_opportunistic_pairing(dev, apdev, params):\n+    \"\"\"NAN Pairing setup using opportunistic bootstrapping\"\"\"\n+    run_nan_pairing_verification(\"PASN\")\n+\n+def test_nan_sae_pairing(dev, apdev, params):\n+    \"\"\"NAN Pairing setup using a password (SAE)\"\"\"\n+    run_nan_pairing_verification(\"SAE\", \"nanpassword\")\n+\n+def test_nan_publish_with_pmk(dev, apdev, params):\n+    \"\"\"NAN publish with PMK and cipher suites\"\"\"\n+    with hwsim_nan_radios(count=2) as [wpas1, wpas2], \\\n+        NanDevice(wpas1, \"nan0\") as pub, NanDevice(wpas2, \"nan1\") as sub:\n+        paddr = pub.wpas.own_addr()\n+\n+        # Test PMK - 32 bytes (64 hex characters)\n+        nd_pmk = \"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"\n+        cipher_suites = \"1,2\"  # NAN_CS_SK_CCM_128, NAN_CS_SK_GCM_256\n+\n+        # Publish with PMK and cipher suites\n+        pid = pub.publish(\"secure_test\", nd_pmk=nd_pmk, cipher_suites=cipher_suites)\n+        if \"FAIL\" in pid:\n+            raise Exception(f\"Failed to publish with PMK: {pid}\")\n+\n+        logger.info(f\"Published with PMK, ID: {pid}\")\n+\n+        # Subscribe to the service\n+        sid = sub.subscribe(\"secure_test\", ssi=None)\n+\n+        # Wait for discovery with PMKIDs advertised\n+        ev = sub.wpas.wait_event([\"NAN-DISCOVERY-RESULT\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-DISCOVERY-RESULT event not seen\")\n+\n+        if f\"address={paddr}\" not in ev:\n+            raise Exception(f\"Unexpected publisher address in discovery: {ev}\")\n+\n+        if f\"publish_id={pid}\" not in ev:\n+            raise Exception(f\"Unexpected publish ID in discovery: {ev}\")\n+\n+        logger.info(f\"Discovery event: {ev}\")\n+\n+        pub.cancel_publish(pid)\n+        sub.cancel_subscribe(sid)\n+\n+def _run_nan_pairing_bootstrap_ndp(pairing_type, password=None, csid=1):\n+    \"\"\"Run NAN pairing bootstrap followed by NDP setup with security\"\"\"\n+    with hwsim_nan_radios() as (wpas1, wpas2), \\\n+        NanDevice(wpas1, \"nan0\", \"ndi0\") as pub, NanDevice(wpas2, \"nan1\", \"ndi1\") as sub:\n+        paddr = pub.wpas.own_addr()\n+        saddr = sub.wpas.own_addr()\n+\n+        # Configure schedules for both devices\n+        if \"OK\" not in sub.schedule_config((2437, \"0e000000\")):\n+            raise Exception(\"Failed to configure schedule (sub)\")\n+\n+        if \"OK\" not in pub.schedule_config((2437, \"0e000000\")):\n+            raise Exception(\"Failed to configure schedule (pub)\")\n+\n+        # Step 1: Pre-bootstrap discovery\n+        pssi = \"aabbccdd\"\n+        sssi = \"ddbbccaa\"\n+\n+        pid = pub.publish(\"test_service\", ssi=pssi, unsolicited=0, pbm=0x1)\n+        sid = sub.subscribe(\"test_service\", ssi=sssi)\n+\n+        logger.info(f\"Publish ID: {pid}, Subscribe ID: {sid}\")\n+\n+        ev = sub.wpas.wait_event([\"NAN-DISCOVERY-RESULT\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-DISCOVERY-RESULT event not seen\")\n+\n+        nan_sync_verify_event(ev, paddr, pid, sid, pssi)\n+\n+        ev = pub.wpas.wait_event([\"NAN-REPLIED\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-REPLIED event not seen\")\n+\n+        nan_sync_verify_event(ev, saddr, pid, sid, sssi)\n+\n+        # Step 2: Bootstrapping\n+        sub.bootstrap(paddr, sid, pid, 0x1)\n+\n+        ev = sub.wpas.wait_event([\"NAN-BOOTSTRAP-SUCCESS\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-SUCCESS event not seen on subscriber\")\n+\n+        ev = pub.wpas.wait_event([\"NAN-BOOTSTRAP-SUCCESS\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-BOOTSTRAP-SUCCESS event not seen on publisher\")\n+\n+        # Step 3: Pairing\n+        nd_pmk = run_nan_pairing(sub, pub, pid, sid, pairing_type, password)\n+\n+        logger.info(f\"Pairing completed successfully: nd_pmk={nd_pmk}, now setting up NDP\")\n+\n+        # Step 4: NDP setup with security using the derived nd_pmk\n+        ndp_id, init_ndi = _nan_ndp_request_and_accept(pub, sub, pid, sid, paddr, saddr,\n+                                                       req_ssi=\"aabbcc\", resp_ssi=\"ddeeff\", csid=csid,\n+                                                       pmk=nd_pmk, configure_schedule=True)\n+\n+        logger.info(\"NDP connection established successfully after pairing\")\n+\n+        _nan_test_connectivity(pub, sub)\n+        _nan_ndp_terminate(pub, sub, paddr, init_ndi, ndp_id)\n+\n+        # Once the NDP is removed, verify that service discovery still works\n+        pub.wpas.dump_monitor()\n+        sub.wpas.dump_monitor()\n+        time.sleep(1)\n+\n+        ev = sub.wpas.wait_event([\"NAN-DISCOVERY-RESULT\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-DISCOVERY-RESULT event not seen\")\n+\n+        nan_sync_verify_event(ev, paddr, pid, sid, pssi)\n+\n+        ev = pub.wpas.wait_event([\"NAN-REPLIED\"], timeout=2)\n+        if ev is None:\n+            raise Exception(\"NAN-REPLIED event not seen\")\n+\n+        nan_sync_verify_event(ev, saddr, pid, sid, sssi)\n+\n+def test_nan_pairing_bootstrap_ndp_sk_ccmp128(dev, apdev, params):\n+    \"\"\"NAN Pairing bootstrap followed by NDP with SK CCMP-128 security\"\"\"\n+    set_country(\"US\")\n+    try:\n+        _run_nan_pairing_bootstrap_ndp(\"SAE\", password=\"password123\", csid=1)\n+    finally:\n+        set_country(\"00\")\n","prefixes":["RFC","92/92"]}