[PATCH 1/3] mesh: add support for SAE Hash-to-Element in mesh mode.

Nicolas Cavallari nicolas.cavallari at green-communications.fr
Thu Sep 18 08:31:38 PDT 2025


It currently comes with the limitation that the PT is derived twice:
one for the supplicant and one for the authenticator.

Signed-off-by: Nicolas Cavallari <nicolas.cavallari at green-communications.fr>
---
 src/ap/ap_config.h        |  1 +
 src/ap/ieee802_11.c       | 14 +++++++++++++-
 wpa_supplicant/mesh.c     |  9 +++++++++
 wpa_supplicant/mesh_rsn.c | 26 +++++++++++++++++++++++---
 wpa_supplicant/mesh_rsn.h |  1 +
 5 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index f38676ad8..3feab92d2 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -53,6 +53,7 @@ struct mesh_conf {
 	unsigned int pairwise_cipher;
 	unsigned int group_cipher;
 	unsigned int mgmt_group_cipher;
+	unsigned int sae_pwe;
 	int dot11MeshMaxRetries;
 	int dot11MeshRetryTimeout; /* msec */
 	int dot11MeshConfirmTimeout; /* msec */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index ca5ca9687..6f43d373e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1594,7 +1594,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 		    sta->sae->tmp) {
 			pos = mgmt->u.auth.variable;
 			end = ((const u8 *) mgmt) + len;
-			if (pos + sizeof(le16) > end) {
+			if (pos + sizeof(le16) +
+			    (sta->sae->h2e ? 3 : 0) > end) {
 				wpa_printf(MSG_ERROR,
 					   "SAE: Too short anti-clogging token request");
 				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1608,6 +1609,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 				goto reply;
 			}
 			pos += sizeof(le16);
+			if (sta->sae->h2e &&
+			    !(pos[0] == WLAN_EID_EXTENSION &&
+			      pos[1] == end - pos - 2 &&
+			      pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN)) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Invalid anti-clogging token container");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+			if (sta->sae->h2e)
+				pos += 3;
 
 			wpabuf_free(sta->sae->tmp->anti_clogging_token);
 			sta->sae->tmp->anti_clogging_token =
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 297d644e5..fdf5e68e3 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -97,6 +97,7 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
 			MESH_CONF_SEC_AMPE;
 	else
 		conf->security |= MESH_CONF_SEC_NONE;
+	conf->sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 	conf->ieee80211w = ssid->ieee80211w;
 	if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
 		if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
@@ -200,6 +201,13 @@ static int wpas_mesh_init_rsn(struct wpa_supplicant *wpa_s)
 
 	len = os_strlen(password);
 	bss->conf->ssid.wpa_passphrase = dup_binstr(password, len);
+	if (ssid->pt) {
+		sae_deinit_pt(bss->conf->ssid.pt);
+		bss->conf->ssid.pt = sae_derive_pt(bss->conf->sae_groups,
+						   ssid->ssid, ssid->ssid_len,
+						   (const u8 *) password,
+						   len, ssid->sae_password_id);
+	}
 
 	wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, ifmsh->mconf);
 	return !wpa_s->mesh_rsn ? -1 : 0;
@@ -480,6 +488,7 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
 	bss->conf->mesh = MESH_ENABLED;
 	bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
 	bss->conf->mesh_fwding = wpa_s->conf->mesh_fwding;
+	bss->conf->sae_pwe = wpas_get_ssid_sae_pwe(wpa_s, ssid);
 
 	ieee80211_freq_to_chan(freq->center_freq1, &chan);
 	if (wpa_s->mesh_vht_enabled) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index ada53c796..b8854163d 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -24,6 +24,7 @@
 #include "wpas_glue.h"
 #include "mesh_mpm.h"
 #include "mesh_rsn.h"
+#include "config.h"
 
 #define MESH_AUTH_TIMEOUT 10
 #define MESH_AUTH_RETRY 3
@@ -187,6 +188,9 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
 	conf.ieee80211w = ieee80211w;
 	if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
 		conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
+#ifdef CONFIG_SAE
+	conf.sae_pwe = rsn->sae_pwe;
+#endif
 #ifdef CONFIG_OCV
 	conf.ocv = ocv;
 #endif /* CONFIG_OCV */
@@ -261,6 +265,7 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
 	mesh_rsn->pairwise_cipher = conf->pairwise_cipher;
 	mesh_rsn->group_cipher = conf->group_cipher;
 	mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher;
+	mesh_rsn->sae_pwe = conf->sae_pwe;
 
 	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr,
 				 conf->ieee80211w, conf->ocv) < 0) {
@@ -341,6 +346,7 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
 				     struct sta_info *sta)
 {
 	const char *password;
+	bool use_pt = false;
 
 	password = ssid->sae_password;
 	if (!password)
@@ -355,14 +361,28 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
 		return -1;
 	}
 
+	if (ssid->sae_password_id &&
+	    wpa_s->mesh_rsn->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
+		use_pt = true;
+	if (wpa_s->mesh_rsn->sae_pwe == SAE_PWE_HASH_TO_ELEMENT)
+		use_pt = true;
+
+	if (!use_pt)
+		return sae_prepare_commit(wpa_s->own_addr, sta->addr,
+					  (u8 *) password, os_strlen(password),
+					  sta->sae);
+
 	if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) {
 		sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id);
 		if (!sta->sae->tmp->pw_id)
 			return -1;
 	}
-	return sae_prepare_commit(wpa_s->own_addr, sta->addr,
-				  (u8 *) password, os_strlen(password),
-				  sta->sae);
+
+	if (!ssid->pt)
+		wpa_s_setup_sae_pt(wpa_s, ssid, true);
+
+	return sae_prepare_commit_pt(sta->sae, ssid->pt,
+				     wpa_s->own_addr, sta->addr, NULL, NULL);
 }
 
 
diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h
index 8775cedc3..e0c88b70d 100644
--- a/wpa_supplicant/mesh_rsn.h
+++ b/wpa_supplicant/mesh_rsn.h
@@ -24,6 +24,7 @@ struct mesh_rsn {
 #ifdef CONFIG_SAE
 	struct wpabuf *sae_token;
 	int sae_group_index;
+	int sae_pwe;
 #endif /* CONFIG_SAE */
 };
 
-- 
2.51.0




More information about the Hostap mailing list