[PATCH 25/29] EPPKE: Update RSNE construction and validation per IEEE P802.11bi/D2.0

Sai Pratyusha Magam smagam at qti.qualcomm.com
Thu Dec 11 05:14:39 PST 2025


From: Ainy Kumari <ainy.kumari at oss.qualcomm.com>

Update existing PASN APIs for RSNE generation and validation logic to
align with IEEE 802.11bi/D2.0, section 12.16.9.3.2 to support EPPKE
authentication.

Below are the key changes:
- Add wpa_pick_group_mgmt_cipher() to select group management cipher.
- Extend wpa_pasn_add_rsne() and wpa_pasn_validate_rsne() to support
  Group Data and Group Management Cipher Suites inclusion for EPPKE.
- Pass group cipher parameters in PASN initiator/responder mode.
- Skip MFPR bit setting in RSN Capabilities for EPPKE.
- Ensure RSNE reflects correct capabilities and cipher suites based
  on auth type

Signed-off-by: Sai Pratyusha Magam <smagam at qti.qualcomm.com>
Signed-off-by: Ainy Kumari <ainy.kumari at oss.qualcomm.com>
---
 src/ap/ieee802_11.c       |  2 +-
 src/common/wpa_common.c   | 70 +++++++++++++++++++++++++++++----------
 src/common/wpa_common.h   |  7 ++--
 src/pasn/pasn_common.h    |  2 ++
 src/pasn/pasn_initiator.c | 14 +++++---
 src/pasn/pasn_responder.c | 22 ++++++++----
 wpa_supplicant/sme.c      |  7 ++++
 7 files changed, 93 insertions(+), 31 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index df7ec8270..545a6b7bf 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2920,7 +2920,7 @@ static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
 		return -1;
 	}
 
-	ret = wpa_pasn_validate_rsne(&rsne_data);
+	ret = wpa_pasn_validate_rsne(&rsne_data, false);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
 		return -1;
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 29cd21515..9a8064d6b 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -3348,6 +3348,20 @@ int wpa_pick_group_cipher(int ciphers)
 }
 
 
+int wpa_pick_group_mgmt_cipher(int ciphers)
+{
+	if (ciphers & WPA_CIPHER_AES_128_CMAC)
+		return WPA_CIPHER_AES_128_CMAC;
+	if (ciphers & WPA_CIPHER_BIP_GMAC_128)
+		return WPA_CIPHER_BIP_GMAC_128;
+	if (ciphers & WPA_CIPHER_BIP_GMAC_256)
+		return WPA_CIPHER_BIP_GMAC_256;
+	if (ciphers & WPA_CIPHER_BIP_CMAC_256)
+		return WPA_CIPHER_BIP_CMAC_256;
+	return -1;
+}
+
+
 int wpa_parse_cipher(const char *value)
 {
 	int val = 0, last;
@@ -4023,13 +4037,15 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
 
 
 /*
- * wpa_pasn_add_rsne - Add an RSNE for PASN authentication
+ * wpa_pasn_add_rsne - Add an RSNE for PASN/EPPKE authentication
  * @buf: Buffer in which the IE will be added
  * @pmkid: Optional PMKID. Can be NULL.
  * @akmp: Authentication and key management protocol
  * @cipher: The cipher suite
+ * @is_eppke: EPPKE Authentication
  */
-int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher)
+int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher,
+		      bool is_eppke, int group_cipher, int group_mgmt_cipher)
 {
 	struct rsn_ie_hdr *hdr;
 	u32 suite;
@@ -4051,8 +4067,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher)
 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
 	pos = (u8 *) (hdr + 1);
 
-	/* Group addressed data is not allowed */
-	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	if (is_eppke) {
+		/* EPPKE: Group addressed data is allowed */
+		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher));
+	} else {
+		/* PASN: Group addressed data is not allowed */
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	}
 	pos += RSN_SELECTOR_LEN;
 
 	/* Add the pairwise cipher */
@@ -4103,8 +4124,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher)
 	}
 	pos += RSN_SELECTOR_LEN;
 
-	/* RSN Capabilities: PASN mandates both MFP capable and required */
-	capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
+	if (is_eppke) {
+		/* RSN Capabilities: EPPKE does not mandate setting MFPR to 1 */
+		capab = WPA_CAPABILITY_MFPC;
+	} else {
+		/* RSN Capabilities: PASN mandates both MFP capable and required */
+		capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
+	}
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
@@ -4120,9 +4146,13 @@ int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher)
 		pos += 2;
 	}
 
-	/* Group addressed management is not allowed */
-	RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
-
+	if (is_eppke) {
+		/* EPPKE: Group addressed management is allowed */
+		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, group_mgmt_cipher));
+	} else {
+		/* PASN: Group addressed management is not allowed */
+		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+	}
 	return 0;
 }
 
@@ -4260,23 +4290,29 @@ int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
 
 
 /*
- * wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE
+ * wpa_pasn_validate_rsne - Validate PASN/EPPKE specific data of RSNE
  * @data: Parsed representation of an RSNE
+ * @is_eppke: EPPKE Authentication
  * Returns -1 for invalid data; otherwise 0
  */
-int wpa_pasn_validate_rsne(const struct wpa_ie_data *data)
+int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke)
 {
-	u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
+	u16 capab = WPA_CAPABILITY_MFPC;
+
+	if (!is_eppke)
+		capab |= WPA_CAPABILITY_MFPR;
 
 	if (data->proto != WPA_PROTO_RSN)
 		return -1;
 
 	if ((data->capabilities & capab) != capab) {
-		wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities");
+		wpa_printf(MSG_DEBUG, "%s: Invalid RSNE capabilities",
+			   is_eppke ? "EPPKE" : "PASN");
 		return -1;
 	}
 
-	if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) {
+	if (!data->has_group ||
+	    (!is_eppke && data->group_cipher != WPA_CIPHER_GTK_NOT_USED)) {
 		wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher");
 		return -1;
 	}
@@ -4307,12 +4343,12 @@ int wpa_pasn_validate_rsne(const struct wpa_ie_data *data)
 	case WPA_KEY_MGMT_PASN:
 		break;
 	default:
-		wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x",
-			   data->key_mgmt);
+		wpa_printf(MSG_ERROR, "%s: invalid key_mgmt: 0x%0x",
+			   is_eppke ? "EPPKE" : "PASN", data->key_mgmt);
 		return -1;
 	}
 
-	if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) {
+	if (!is_eppke && (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED)) {
 		wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher");
 		return -1;
 	}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index ad3c017ec..3739f94f8 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -761,6 +761,7 @@ int rsn_cipher_put_suites(u8 *pos, int ciphers);
 int wpa_cipher_put_suites(u8 *pos, int ciphers);
 int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
 int wpa_pick_group_cipher(int ciphers);
+int wpa_pick_group_mgmt_cipher(int ciphers);
 int wpa_parse_cipher(const char *value);
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
@@ -793,8 +794,8 @@ void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
 				const u8 *src, const u8 *dst,
 				u8 trans_seq, u16 status, bool is_eppke);
 
-int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid,
-		      int akmp, int cipher);
+int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher,
+		      bool is_eppke, int group_cipher, int group_mgmt_cipher);
 
 void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
 			       u8 wrapped_data_format,
@@ -804,7 +805,7 @@ void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
 int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
 			      struct wpabuf *wrapped_data_buf);
 
-int wpa_pasn_validate_rsne(const struct wpa_ie_data *data);
+int wpa_pasn_validate_rsne(const struct wpa_ie_data *data, bool is_eppke);
 int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap,
 				struct wpa_pasn_params_data *pasn_params);
 
diff --git a/src/pasn/pasn_common.h b/src/pasn/pasn_common.h
index 4d35bffd0..d75255709 100644
--- a/src/pasn/pasn_common.h
+++ b/src/pasn/pasn_common.h
@@ -55,6 +55,8 @@ struct pasn_data {
 	u8 mld_addr[ETH_ALEN];
 	bool is_ml_sta;
 	u32 hash_alg;
+	int group_cipher;
+	int group_mgmt_cipher;
 
 #ifdef CONFIG_SAE
 	struct sae_pt *pt;
diff --git a/src/pasn/pasn_initiator.c b/src/pasn/pasn_initiator.c
index 32daf9c05..4ce5f6549 100644
--- a/src/pasn/pasn_initiator.c
+++ b/src/pasn/pasn_initiator.c
@@ -296,7 +296,9 @@ static struct wpabuf * wpas_pasn_fils_build_auth(struct pasn_data *pasn)
 	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 
 	/* Own RSNE */
-	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher,
+			  pasn->auth_alg == WLAN_AUTH_EPPKE,
+			  pasn->group_cipher, pasn->group_mgmt_cipher);
 
 	/* FILS Nonce */
 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
@@ -422,7 +424,8 @@ static int wpas_pasn_wd_fils_rx(struct pasn_data *pasn, struct wpabuf *wd)
 		return -1;
 	}
 
-	ret = wpa_pasn_validate_rsne(&rsne_data);
+	ret = wpa_pasn_validate_rsne(&rsne_data,
+				     pasn->auth_alg == WLAN_AUTH_EPPKE);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
 		return -1;
@@ -645,7 +648,9 @@ struct wpabuf *wpas_pasn_build_auth_1(struct pasn_data *pasn,
 			wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
 	}
 
-	if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
+	if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher,
+			      pasn->auth_alg == WLAN_AUTH_EPPKE,
+			      pasn->group_cipher, pasn->group_mgmt_cipher) < 0)
 		goto fail;
 
 	if (!wrapped_data_buf)
@@ -1261,7 +1266,8 @@ int wpas_parse_pasn_frame(struct pasn_data *pasn, u16 auth_type,
 		goto fail;
 	}
 
-	ret = wpa_pasn_validate_rsne(&rsn_data);
+	ret = wpa_pasn_validate_rsne(&rsn_data,
+				     pasn->auth_alg == WLAN_AUTH_EPPKE);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
 		goto fail;
diff --git a/src/pasn/pasn_responder.c b/src/pasn/pasn_responder.c
index b4dea5ee5..0b7426c7e 100644
--- a/src/pasn/pasn_responder.c
+++ b/src/pasn/pasn_responder.c
@@ -303,7 +303,9 @@ static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn)
 	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 
 	/* Own RSNE */
-	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+	wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher,
+			  pasn->auth_alg == WLAN_AUTH_EPPKE,
+			  pasn->group_cipher, pasn->group_mgmt_cipher);
 
 	/* FILS Nonce */
 	wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
@@ -558,8 +560,9 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
 #endif /* CONFIG_FILS */
 	}
 
-	if (wpa_pasn_add_rsne(buf, pmkid,
-			      pasn->akmp, pasn->cipher) < 0)
+	if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher,
+			      pasn->auth_alg == WLAN_AUTH_EPPKE,
+			      pasn->group_cipher, pasn->group_mgmt_cipher) < 0)
 		goto fail;
 
 	/* No need to derive PMK if PMKSA is given */
@@ -636,8 +639,10 @@ int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr,
 		if (!rsn_buf)
 			goto fail;
 
-		if (wpa_pasn_add_rsne(rsn_buf, pmkid,
-				      pasn->akmp, pasn->cipher) < 0)
+		if (wpa_pasn_add_rsne(rsn_buf, pmkid, pasn->akmp, pasn->cipher,
+				      pasn->auth_alg == WLAN_AUTH_EPPKE,
+				      pasn->group_cipher,
+				      pasn->group_mgmt_cipher) < 0)
 			goto fail;
 
 		rsn_ie = wpabuf_head_u8(rsn_buf);
@@ -762,7 +767,8 @@ int handle_auth_pasn_1(struct pasn_data *pasn,
 		goto send_resp;
 	}
 
-	ret = wpa_pasn_validate_rsne(&rsn_data);
+	ret = wpa_pasn_validate_rsne(&rsn_data,
+				     pasn->auth_alg == WLAN_AUTH_EPPKE);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
 		status = WLAN_STATUS_INVALID_RSNIE;
@@ -790,6 +796,10 @@ int handle_auth_pasn_1(struct pasn_data *pasn,
 
 	pasn->akmp = rsn_data.key_mgmt;
 	pasn->cipher = rsn_data.pairwise_cipher;
+#ifdef CONFIG_ENC_ASSOC
+	pasn->group_cipher = rsn_data.group_cipher;
+	pasn->group_mgmt_cipher = rsn_data.mgmt_group_cipher;
+#endif /* CONFIG_ENC_ASSOC */
 
 	if (pasn->derive_kdk &&
 	    ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index fa81d6f96..1187484ef 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -631,6 +631,7 @@ static void wpas_eppke_initialize(struct wpa_supplicant *wpa_s, struct wpa_bss *
 	u8 beacon_rsne_len, beacon_rsnxe_len;
 	u32 capab = 0;
 	int group;
+	int group_mgmt_cipher;
 
 	pasn = &wpa_s->pasn;
 
@@ -710,6 +711,12 @@ static void wpas_eppke_initialize(struct wpa_supplicant *wpa_s, struct wpa_bss *
 	}
 	pasn->akmp = wpa_s->key_mgmt;
 	pasn->cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 1);
+	pasn->group_cipher = wpa_pick_group_cipher(ssid->group_cipher);
+	if (ssid->group_mgmt_cipher != 0)
+		group_mgmt_cipher = ssid->group_mgmt_cipher;
+	else
+		group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+	pasn->group_mgmt_cipher = wpa_pick_group_mgmt_cipher(group_mgmt_cipher);
 	pasn->group = group;
 	pasn->freq = bss->freq;
 	pasn->auth_alg = WLAN_AUTH_EPPKE;
-- 
2.34.1




More information about the Hostap mailing list