[PATCH 16/34] PR: Add support to initiate PASN authentication for Proximity Ranging
Peddolla Harshavardhan Reddy
peddolla at qti.qualcomm.com
Thu Jun 19 08:37:09 PDT 2025
Proximity Ranging negotiation is performed by piggybacking PR
Information Elements (IEs) onto PASN frames. These changes are
introduced to initiate PASN and enable this negotiation process.
The negotiation determines the roles, the type of ranging, and
the channel on which the ranging should be conducted.
Signed-off-by: Peddolla Harshavardhan Reddy <peddolla at qti.qualcomm.com>
---
src/common/proximity_ranging.c | 125 +++++++++++++++++++++++++++
src/common/proximity_ranging.h | 42 ++++++++++
wpa_supplicant/pr_supplicant.c | 135 ++++++++++++++++++++++++++++++
wpa_supplicant/pr_supplicant.h | 13 +++
wpa_supplicant/wpa_supplicant_i.h | 1 +
5 files changed, 316 insertions(+)
diff --git a/src/common/proximity_ranging.c b/src/common/proximity_ranging.c
index 11693dc5d..580e97f7e 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);
}
@@ -971,3 +979,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 85a5727a3..3448c5296 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
*/
@@ -189,6 +195,21 @@ enum pr_attr_id {
#define PR_ISTA_SUPPORT BIT(0)
#define PR_RSTA_SUPPORT BIT(1)
+/**
+ * PASN capabilities
+ * Proximity Ranging Implementation Considerations for P2P Operation 2025,
+ * 3.3.2, Table 7.
+ */
+#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];
@@ -226,6 +247,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;
};
@@ -332,6 +357,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 {
@@ -371,5 +410,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 c41e339f9..00ab9f341 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);
@@ -133,6 +144,8 @@ int wpas_pr_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
pr.support_6ghz = wpas_is_6ghz_supported(wpa_s, true);
+ 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, pr.support_6ghz);
@@ -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 e263c17bb..499f640e9 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 f1b0f31ae..bee6b9e61 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1613,6 +1613,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