]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
PR: PASN authentication initialization for Proximity Ranging
authorPeddolla Harshavardhan Reddy <peddolla@qti.qualcomm.com>
Mon, 28 Apr 2025 09:50:50 +0000 (15:20 +0530)
committerJouni Malinen <j@w1.fi>
Fri, 17 Oct 2025 10:22:51 +0000 (13:22 +0300)
Proximity Ranging negotiation is performed by wrapping PR element(s)
onto PASN Authentication frames. Add functionality 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@qti.qualcomm.com>
src/common/proximity_ranging.c
src/common/proximity_ranging.h
src/pasn/pasn_common.h
wpa_supplicant/pr_supplicant.c
wpa_supplicant/pr_supplicant.h
wpa_supplicant/wpa_supplicant_i.h

index 28475e624833da3349a0343e9c1802fd6a11cb1c..fea0c3ddab747f7bcdef3f3995199517d46bdb82 100644 (file)
@@ -12,6 +12,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "crypto/sha256.h"
+#include "pasn/pasn_common.h"
 #include "proximity_ranging.h"
 
 
@@ -23,6 +24,12 @@ static bool valid_country_ch(char c)
 
 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);
 }
 
@@ -1029,3 +1036,129 @@ 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,
+                             u8 ranging_type)
+{
+       struct pasn_data *pasn;
+
+       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;
+
+       /* As specified in Proximity Ranging Implementation Considerations for
+        * P2P Operation D1.8, unauthenticated mode PASN with DH group 19
+        * should be supported by all P2P proximity ranging devices. */
+       if (!(pr->cfg->pasn_type & BIT(0)) ||
+           !(dev->pr_caps.pasn_type & BIT(0))) {
+               wpa_printf(MSG_DEBUG,
+                          "PR PASN: Unauthenticated DH group 19 NOT supported, PASN type of self 0x%x, peer 0x%x",
+                          pr->cfg->pasn_type, dev->pr_caps.pasn_type);
+               return -1;
+       }
+
+       /* As specified in Proximity Ranging Implementation Considerations for
+        * P2P Operation D1.8, EDCA based ranging is only supported with
+        * unauthenticated mode PASN with DH group 19. */
+       if (((pr->cfg->pasn_type & 0xc) && (dev->pr_caps.pasn_type & 0xc)) &&
+           ranging_type != PR_EDCA_BASED_RANGING) {
+               pasn->group = 20;
+               pasn->cipher = WPA_CIPHER_GCMP_256;
+       } else {
+               pasn->group = 19;
+               pasn->cipher = WPA_CIPHER_CCMP;
+       }
+
+       if (pr->cfg->secure_he_ltf &&
+           ranging_type == PR_NTB_SECURE_LTF_BASED_RANGING) {
+               pasn->secure_ltf = true;
+               pasn_enable_kdk_derivation(pasn);
+       } else {
+               pasn_disable_kdk_derivation(pasn);
+       }
+       wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+       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, "PR PASN: Peer address NULL");
+               return -1;
+       }
+
+       dev = pr_get_device(pr, addr);
+       if (!dev) {
+               wpa_printf(MSG_DEBUG, "PR PASN: 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, ranging_type)) {
+               wpa_printf(MSG_INFO, "PR PASN: Initialization 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_INFO, "PR PASN: Failed to start PASN");
+
+       return ret;
+}
+
+#endif /* CONFIG_PASN */
index 5eedcd674f2a8cc3c59a36a8fc8c9a178cad5923..28f66742217eb79651cb56ec1953ee2af5954552 100644 (file)
  */
 #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
  */
@@ -226,6 +232,21 @@ enum pr_attr_id {
 #define PR_ISTA_SUPPORT BIT(0)
 #define PR_RSTA_SUPPORT BIT(1)
 
+/*
+ * PASN capabilities in PASN Type field
+ * Proximity Ranging Implementation Considerations for P2P Operation D1.8,
+ * Table 7 (Proximity Ranging Capability Attribute).
+ */
+#define PR_PASN_DH19_UNAUTH BIT(0)
+#define PR_PASN_DH19_AUTH BIT(1)
+#define PR_PASN_DH20_UNAUTH BIT(2)
+#define PR_PASN_DH20_AUTH 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];
@@ -263,6 +284,12 @@ struct pr_device {
         */
        u8 pmk[PMK_LEN_MAX];
        bool pmk_valid;
+
+#ifdef CONFIG_PASN
+       /* PASN data structure */
+       struct pasn_data *pasn;
+       enum pr_pasn_role pasn_role;
+#endif /* CONFIG_PASN */
 };
 
 
@@ -369,6 +396,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 {
@@ -407,5 +448,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);
 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 */
index fd2b71ace43224049dd752bd61d1e0d07c8beb45..182b7655851dc6f2e18114f69e4d5204ce912698 100644 (file)
 #ifndef PASN_COMMON_H
 #define PASN_COMMON_H
 
+#include "common/wpa_common.h"
+#ifdef CONFIG_SAE
+#include "common/sae.h"
+#endif /* CONFIG_SAE */
+#include "crypto/sha384.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
index bf6905e50c92980f21cac3974698a6a726b4bf94..7f7fdf143d9c84411c919f32afe28039226c82e1 100644 (file)
@@ -9,12 +9,18 @@
 #include "includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "common/proximity_ranging.h"
 #include "p2p/p2p.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
+#include "driver_i.h"
 #include "pr_supplicant.h"
 
+#ifdef CONFIG_PASN
+static void wpas_pr_pasn_timeout(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_PASN */
+
 
 static int wpas_pr_edca_get_bw(enum edca_format_and_bw_value format_and_bw)
 {
@@ -239,6 +245,16 @@ wpas_pr_setup_ntb_channels(struct wpa_supplicant *wpa_s,
 }
 
 
+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);
@@ -300,6 +316,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_ntb_channels(wpa_s, &pr.ntb_channels,
@@ -360,6 +378,10 @@ void wpas_pr_deinit(struct wpa_supplicant *wpa_s)
                wpa_s->global->pr = NULL;
                wpa_s->global->pr_init_wpa_s = NULL;
        }
+
+#ifdef CONFIG_PASN
+       eloop_cancel_timeout(wpas_pr_pasn_timeout, wpa_s, NULL);
+#endif /* CONFIG_PASN */
 }
 
 
@@ -384,3 +406,124 @@ 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 */
index 931d307802a35616b7c63f898402a8e40450f69d..920a859ec2529c57cb0b623ccad2cb616621f16f 100644 (file)
@@ -21,6 +21,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 */
 
@@ -50,6 +54,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 */
index 676c017020a30b8ab21199e15a898e33bdb75bed..9780822dfc693caccd6a7b5a2eb42a3a60600e8a 100644 (file)
@@ -1608,6 +1608,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;