[RFC PATCH 16/34] Add support to initiate PASN authentication for Proximity Ranging

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


Signed-off-by: Peddolla Harshavardhan Reddy <peddolla at qti.qualcomm.com>
---
 src/common/proximity_ranging.c    | 125 +++++++++++++++++++++++++++
 src/common/proximity_ranging.h    |  38 +++++++++
 wpa_supplicant/pr_supplicant.c    | 135 ++++++++++++++++++++++++++++++
 wpa_supplicant/pr_supplicant.h    |  13 +++
 wpa_supplicant/wpa_supplicant_i.h |   1 +
 5 files changed, 312 insertions(+)

diff --git a/src/common/proximity_ranging.c b/src/common/proximity_ranging.c
index b2eb42d6b..2bd7ee9fe 100644
--- a/src/common/proximity_ranging.c
+++ b/src/common/proximity_ranging.c
@@ -13,14 +13,22 @@
 #include "common/ieee802_11_common.h"
 #include "common/qca-vendor.h"
 #include "wps/wps_i.h"
+#include "common/sae.h"
 #include "proximity_ranging.h"
 #include "crypto/sha256.h"
 #include "crypto/sha384.h"
 #include "crypto/crypto.h"
+#include "pasn/pasn_common.h"
 
 
 static void pr_device_free(struct pr_data *pr, struct pr_device *dev)
 {
+#ifdef CONFIG_PASN
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+		pasn_data_deinit(dev->pasn);
+	}
+#endif /* CONFIG_PASN */
 	os_free(dev);
 }
 
@@ -973,3 +981,120 @@ void pr_process_usd_elems(struct pr_data *pr, const u8 *ies, u16 ies_len,
 
 	pr_parse_free(&msg);
 }
+
+
+#ifdef CONFIG_PASN
+
+static int pr_pasn_initialize(struct pr_data *pr, struct pr_device *dev,
+			      const u8 *addr, u8 auth_mode, int freq)
+{
+	struct pasn_data *pasn;
+
+	if (!pr || !dev)
+		return -1;
+
+	if (dev->pasn) {
+		wpa_pasn_reset(dev->pasn);
+	} else {
+		dev->pasn = pasn_data_init();
+		if (!dev->pasn)
+			return -1;
+	}
+
+	pasn = dev->pasn;
+	os_memcpy(pasn->own_addr, pr->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(pasn->peer_addr, addr, ETH_ALEN);
+
+	if (dev->pasn_role == PR_ROLE_PASN_INITIATOR) {
+		os_memcpy(pasn->bssid, pasn->peer_addr, ETH_ALEN);
+	} else {
+		os_memcpy(pasn->bssid, pasn->own_addr, ETH_ALEN);
+	}
+
+	pasn->noauth = 1;
+
+	if ((pr->cfg->pasn_type & 0xc) &&
+	    (dev->pr_caps.pasn_type & 0xc)) {
+		pasn->group = 20;
+		pasn->cipher = WPA_CIPHER_GCMP_256;
+	} else {
+		pasn->group = 19;
+		pasn->cipher = WPA_CIPHER_CCMP;
+	}
+
+	if (pr->cfg->secure_he_ltf)
+		pasn_enable_kdk_derivation(pasn);
+	else
+		pasn_disable_kdk_derivation(pasn);
+
+	wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+	if (pr->cfg->secure_he_ltf)
+		pasn->secure_ltf = true;
+
+	if (auth_mode == PR_PASN_AUTH_MODE_SAE) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+	} else if (auth_mode == PR_PASN_AUTH_MODE_PMK) {
+		pasn->akmp = WPA_KEY_MGMT_SAE;
+	} else {
+		pasn->akmp = WPA_KEY_MGMT_PASN;
+	}
+
+	pasn->rsn_pairwise = pasn->cipher;
+	pasn->wpa_key_mgmt = pasn->akmp;
+
+	pasn->cb_ctx = pr->cfg->cb_ctx;
+	pasn->send_mgmt = pr->cfg->pasn_send_mgmt;
+	pasn->freq = freq;
+	return 0;
+}
+
+
+int pr_initiate_pasn_auth(struct pr_data *pr, const u8 *addr, int freq,
+			  u8 auth_mode, u8 ranging_role, u8 ranging_type,
+			  int forced_pr_freq)
+{
+	int ret = 0;
+	struct pasn_data *pasn;
+	struct pr_device *dev;
+
+	if (!addr) {
+		wpa_printf(MSG_DEBUG, "Peer address NULL");
+		return -1;
+	}
+
+	dev = pr_get_device(pr, addr);
+	if (!dev) {
+		wpa_printf(MSG_DEBUG, "Peer not known");
+		return -1;
+	}
+
+	if (freq == 0)
+		freq = dev->listen_freq;
+
+	dev->pasn_role = PR_ROLE_PASN_INITIATOR;
+
+	if (pr_pasn_initialize(pr, dev, addr, auth_mode, freq)) {
+		wpa_printf(MSG_ERROR, "PR PASN: Initialize failed");
+		return -1;
+	}
+	pasn = dev->pasn;
+
+	if (auth_mode == PR_PASN_AUTH_MODE_PMK) {
+		ret = wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr,
+				      pasn->bssid, pasn->akmp, pasn->cipher,
+				      pasn->group, pasn->freq, NULL, 0, NULL, 0,
+				      NULL);
+	} else {
+		ret = wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr,
+				      pasn->bssid, pasn->akmp, pasn->cipher,
+				      pasn->group, pasn->freq, NULL, 0, NULL, 0,
+				      NULL);
+	}
+	if (ret)
+		wpa_printf(MSG_ERROR, "PR: Failed to start PASN");
+
+	return ret;
+}
+
+#endif /* CONFIG_PASN */
diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index 5d5ec0dc3..16ebaabd7 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -38,6 +38,12 @@
 #define PR_MAX_PEER 100
 
 
+enum pr_pasn_role {
+	PR_ROLE_IDLE = 0,
+	PR_ROLE_PASN_INITIATOR,
+	PR_ROLE_PASN_RESPONDER,
+};
+
 /**
  * struct pr_channels - List of supported channels
  */
@@ -171,6 +177,17 @@ enum pr_attr_id {
 #define PR_ISTA_SUPPORT BIT(0)
 #define PR_RSTA_SUPPORT BIT(1)
 
+/* PASN capabilities*/
+#define PR_PASN_DH19_AUTH BIT(0)
+#define PR_PASN_DH19_UNAUTH BIT(1)
+#define PR_PASN_DH20_AUTH BIT(2)
+#define PR_PASN_DH20_UNAUTH BIT(3)
+
+/* Authentication Mode */
+#define PR_PASN_AUTH_MODE_PASN   0
+#define PR_PASN_AUTH_MODE_SAE    1
+#define PR_PASN_AUTH_MODE_PMK    2
+
 struct pr_dev_ik {
 	struct dl_list list;
 	u8 dik[DEVICE_IDENTITY_KEY_LEN];
@@ -208,6 +225,10 @@ struct pr_device {
 	 */
 	u8 pmk[PMK_LEN_MAX];
 	bool pmk_valid;
+
+	/* PASN data structure */
+	struct pasn_data *pasn;
+	enum pr_pasn_role pasn_role;
 };
 
 
@@ -312,6 +333,20 @@ struct pr_config {
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
+
+	/**
+	 * pasn_send_mgmt - Function handler to transmit a Management frame
+	 * @ctx: Callback context from cb_ctx
+	 * @data: Frame to transmit
+	 * @data_len: Length of frame to transmit
+	 * @noack: No ack flag
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @wait: How many milliseconds to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*pasn_send_mgmt)(void *ctx, const u8 *data, size_t data_len,
+			      int noack, unsigned int freq, unsigned int wait);
+
 };
 
 struct pr_data {
@@ -351,5 +386,8 @@ void pr_add_dev_ik(struct pr_data *pr, const u8 *dik, const char *password,
 struct wpabuf * pr_prepare_usd_elems(struct pr_data *pr, const char *country);
 void pr_process_usd_elems(struct pr_data *pr, const u8 *ies, u16 ies_len,
 			  const u8 *peer_addr, unsigned int freq);
+int pr_initiate_pasn_auth(struct pr_data *pr, const u8 *addr, int freq,
+			  u8 auth_mode, u8 ranging_role, u8 ranging_type,
+			  int forced_pr_freq);
 
 #endif /* PROXIMITY_RANGING_H */
diff --git a/wpa_supplicant/pr_supplicant.c b/wpa_supplicant/pr_supplicant.c
index 18eebea73..0ec4ca828 100644
--- a/wpa_supplicant/pr_supplicant.c
+++ b/wpa_supplicant/pr_supplicant.c
@@ -74,6 +74,17 @@ static int wpas_pr_setup_channels(struct wpa_supplicant *wpa_s,
 	return 0;
 }
 
+
+static int wpas_pr_pasn_send_mgmt(void *ctx, const u8 *data, size_t data_len,
+				  int noack, unsigned int freq,
+				  unsigned int wait)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_send_mlme(wpa_s, data, data_len, noack, freq, wait);
+}
+
+
 struct wpabuf * wpas_pr_usd_elems(struct wpa_supplicant *wpa_s)
 {
 	return pr_prepare_usd_elems(wpa_s->global->pr, wpa_s->conf->country);
@@ -132,6 +143,8 @@ int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 
 	pr.support_6ghz = false;
 
+	pr.pasn_send_mgmt = wpas_pr_pasn_send_mgmt;
+
 	pr.secure_he_ltf = wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA;
 
 	wpas_pr_setup_channels(wpa_s, &pr.ntb_channels);
@@ -213,3 +226,125 @@ void wpas_pr_set_dev_ik(struct wpa_supplicant *wpa_s, const u8 *dik,
 
 	pr_add_dev_ik(pr, dik, password, pmk, own);
 }
+
+#ifdef CONFIG_PASN
+
+struct wpa_pr_pasn_auth_work {
+	u8 peer_addr[ETH_ALEN];
+	u8 auth_mode;
+	int freq;
+	enum pr_pasn_role role;
+	u8 ranging_role;
+	u8 ranging_type;
+	u8 *ssid;
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+	int forced_pr_freq;
+};
+
+
+static void wpas_pr_pasn_free_auth_work(struct wpa_pr_pasn_auth_work *awork)
+{
+	if (!awork)
+		return;
+	os_free(awork->ssid);
+	os_free(awork);
+}
+
+
+static void wpas_pr_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "PR PASN: Cancel pr-pasn-start-auth work");
+
+	/* Remove pending/started work */
+	radio_remove_works(wpa_s, "pr-pasn-start-auth", 0);
+}
+
+
+static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->pr_pasn_auth_work) {
+		wpas_pr_pasn_cancel_auth_work(wpa_s);
+		wpa_s->pr_pasn_auth_work = NULL;
+	}
+	wpa_printf(MSG_DEBUG, "PR: PASN timed out");
+}
+
+
+static void wpas_pr_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	int ret;
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_pr_pasn_auth_work *awork = work->ctx;
+	struct pr_data *pr = wpa_s->global->pr;
+	const u8 *peer_addr = NULL;
+
+	if (deinit) {
+		if (!work->started)
+			eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
+
+		wpas_pr_pasn_free_auth_work(awork);
+		return;
+	}
+
+	if (!is_zero_ether_addr(awork->peer_addr))
+		peer_addr = awork->peer_addr;
+
+	ret = pr_initiate_pasn_auth(pr, peer_addr, awork->freq,
+				    awork->auth_mode, awork->ranging_role,
+				    awork->ranging_type, awork->forced_pr_freq);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PR PASN: Failed to start PASN authentication");
+		goto fail;
+	}
+	eloop_cancel_timeout(wpas_pr_pasn_timeout,
+			     wpa_s, NULL);
+	eloop_register_timeout(2, 0,
+			       wpas_pr_pasn_timeout,
+			       wpa_s, NULL);
+	wpa_s->pr_pasn_auth_work = work;
+	return;
+
+fail:
+	wpas_pr_pasn_free_auth_work(awork);
+	work->ctx = NULL;
+	radio_work_done(work);
+}
+
+
+int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
+			       const u8 *peer_addr, int freq, u8 auth_mode,
+			       u8 ranging_role, u8 ranging_type,
+			       int forced_pr_freq)
+{
+	struct wpa_pr_pasn_auth_work *awork;
+
+	wpas_pr_pasn_cancel_auth_work(wpa_s);
+	wpa_s->pr_pasn_auth_work = NULL;
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	awork->freq = freq;
+	os_memcpy(awork->peer_addr, peer_addr, ETH_ALEN);
+	awork->ranging_role = ranging_role;
+	awork->ranging_type = ranging_type;
+	awork->auth_mode = auth_mode;
+	awork->forced_pr_freq = forced_pr_freq;
+
+	if (radio_add_work(wpa_s, freq, "pr-pasn-start-auth", 1,
+			   wpas_pr_pasn_auth_start_cb, awork) < 0) {
+		wpas_pr_pasn_free_auth_work(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "PR PASN: Authentication work successfully added");
+	return 0;
+}
+
+#endif /* CONFIG_PASN */
diff --git a/wpa_supplicant/pr_supplicant.h b/wpa_supplicant/pr_supplicant.h
index ff4478ef9..4f082fb5d 100644
--- a/wpa_supplicant/pr_supplicant.h
+++ b/wpa_supplicant/pr_supplicant.h
@@ -22,6 +22,10 @@ struct wpabuf * wpas_pr_usd_elems(struct wpa_supplicant *wpa_s);
 void wpas_pr_process_usd_elems(struct wpa_supplicant *wpa_s, const u8 *buf,
 			       u16 buf_len, const u8 *peer_addr,
 			       unsigned int freq);
+int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
+			       const u8 *peer_addr, int freq, u8 auth_mode,
+			       u8 ranging_role, u8 ranging_type,
+			       int forced_pr_freq);
 #else /* CONFIG_PR */
 static inline int wpas_pr_init(struct wpa_global *global,
 			       struct wpa_supplicant *wpa_s)
@@ -49,6 +53,15 @@ static inline struct wpabuf * wpas_pr_usd_elems(struct wpa_supplicant *wpa_s)
 	return NULL;
 }
 
+static inline int wpas_pr_initiate_pasn_auth(struct wpa_supplicant *wpa_s,
+					     const u8 *peer_addr, int freq,
+					     u8 auth_mode, u8 ranging_role,
+					     u8 ranging_type,
+					     int forced_pr_freq)
+{
+	return 0;
+}
+
 #endif /* CONFIG_PR */
 
 #endif /*PR_SUPPLICANT_H */
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 85515f6fb..d648e6ccc 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1614,6 +1614,7 @@ struct wpa_supplicant {
 #ifdef CONFIG_P2P
 	struct wpa_radio_work *p2p_pasn_auth_work;
 #endif /* CONFIG_P2P */
+	struct wpa_radio_work *pr_pasn_auth_work;
 #endif /* CONFIG_PASN */
 
 	bool is_6ghz_enabled;
-- 
2.34.1




More information about the Hostap mailing list