diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 0781676bde..b158b37cdb 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -4977,3 +4977,14 @@ void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z)
 	}
 }
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_PASN
+void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+			      const u8 *pmkid, const u8 *bssid, int key_mgmt)
+{
+	sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
+					bssid, sm->own_addr, NULL,
+					key_mgmt, 0);
+}
+#endif /* CONFIG_PASN */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 30e7fad83c..f72725902f 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -500,5 +500,7 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
 void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
 void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
 void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
+void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+			      const u8 *pmkid, const u8 *bssid, int key_mgmt);
 
 #endif /* WPA_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a0cacbd25d..1b09c88c15 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -10074,6 +10074,7 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
 	u8 bssid[ETH_ALEN];
 	int akmp = -1, cipher = -1, got_bssid = 0;
 	u16 group = 0xFFFF;
+	int id = 0;
 
 	/*
 	 * Entry format: bssid=<BSSID> akmp=<AKMP> cipher=<CIPHER> group=<group>
@@ -10113,6 +10114,8 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
 			cipher = WPA_CIPHER_GCMP;
 		} else if (os_strncmp(token, "group=", 6) == 0) {
 			group = atoi(token + 6);
+		} else if (os_strncmp(token, "nid=", 4) == 0) {
+			id = atoi(token + 4);
 		} else {
 			wpa_printf(MSG_DEBUG,
 				   "CTRL: PASN Invalid parameter: '%s'",
@@ -10126,7 +10129,8 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
 		return -1;
 	}
 
-	return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group);
+	return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group,
+				    id);
 }
 #endif /* CONFIG_PASN */
 
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
index 2f92024b50..1d94fd6b79 100644
--- a/wpa_supplicant/pasn_supplicant.c
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -21,6 +21,7 @@
 #include "driver_i.h"
 #include "bss.h"
 #include "common/ptksa_cache.h"
+#include "config.h"
 
 static const int dot11RSNAConfigPMKLifetime = 43200;
 
@@ -29,6 +30,7 @@ struct wpa_pasn_auth_work {
 	int akmp;
 	int cipher;
 	u16 group;
+	int network_id;
 };
 
 
@@ -61,14 +63,202 @@ static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		status);
 }
 
+#ifdef CONFIG_SAE
+
+static struct wpabuf *wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct wpabuf *buf = NULL;
+	const char *password = NULL;
+	int ret;
+
+	if (pasn->ssid) {
+		password = pasn->ssid->sae_password;
+		if (!password)
+			password = pasn->ssid->passphrase;
+	}
+
+	if (!password) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+		return NULL;
+	}
+
+	ret = sae_set_group(&pasn->sae, pasn->group);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+		return NULL;
+	}
 
-static struct wpabuf *wpas_pasn_get_wrapped_data(struct wpas_pasn *pasn)
+	ret = sae_prepare_commit(wpa_s->own_addr, pasn->bssid,
+				 (u8 *)password,
+				 os_strlen(password), 0,
+				 &pasn->sae);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+		return NULL;
+	}
+
+	/* Need to add the entire authentication frame body */
+	buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+		return NULL;
+	}
+
+	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	sae_write_commit(&pasn->sae, buf, NULL, 0);
+	pasn->sae.state = SAE_COMMITTED;
+
+	return buf;
+}
+
+
+static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s,
+			       struct wpabuf *wd)
 {
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	const u8 *data;
+	size_t buf_len;
+	u16 len, res, alg, seq, status;
+	int groups[] = { pasn->group, 0 };
+	int ret;
+
+	if (!wd)
+		return -1;
+
+	data = wpabuf_head_u8(wd);
+	buf_len = wpabuf_len(wd);
+
+	/* first handle the commit message */
+	if (buf_len < 2) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
+		return -1;
+	}
+
+	len = WPA_GET_LE16(data);
+	if (len < 6 || buf_len - 2 < len) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
+		return -1;
+	}
+
+	buf_len -= 2;
+	data += 2;
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+		return -1;
+	}
+
+	res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
+			       0);
+	if (res != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
+		return -1;
+	}
+
+	/* process the commit message and derive the PMK */
+	ret = sae_process_commit(&pasn->sae);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+		return -1;
+	}
+
+	buf_len -= len;
+	data += len;
+
+	/* Handle the confirm message */
+	if (buf_len < 2) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
+		return -1;
+	}
+
+	len = WPA_GET_LE16(data);
+	if (len < 6 || buf_len - 2 < len) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
+		return -1;
+	}
+
+	buf_len -= 2;
+	data += 2;
+
+	alg = WPA_GET_LE16(data);
+	seq = WPA_GET_LE16(data + 2);
+	status = WPA_GET_LE16(data + 4);
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE: confirm: alg=%u, seq=%u, status=%u",
+		   alg, seq, status);
+
+	if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer confirm");
+		return -1;
+	}
+
+	res = sae_check_confirm(&pasn->sae, data + 6, len - 6);
+	if (res != WLAN_STATUS_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
+	pasn->sae.state = SAE_ACCEPTED;
+
+	return 0;
+}
+
+
+static struct wpabuf *wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct wpabuf *buf = NULL;
+
+	/* Need to add the entire authentication frame body */
+	buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
+	if (!buf) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+		return NULL;
+	}
+
+	wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	sae_write_confirm(&pasn->sae, buf);
+	pasn->sae.state = SAE_CONFIRMED;
+
+	return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+static struct wpabuf *wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+
+	if (pasn->using_pmksa)
+		return NULL;
+
 	switch (pasn->akmp) {
 	case WPA_KEY_MGMT_PASN:
 		/* no wrapped data */
 		return NULL;
 	case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+		if (pasn->trans_seq == 0)
+			return wpas_pasn_wd_sae_commit(wpa_s);
+		else if (pasn->trans_seq == 2)
+			return wpas_pasn_wd_sae_confirm(wpa_s);
+#endif /* CONFIG_SAE */
+		/* fall through */
 	case WPA_KEY_MGMT_FILS_SHA256:
 	case WPA_KEY_MGMT_FILS_SHA384:
 	case WPA_KEY_MGMT_FT_PSK:
@@ -85,6 +275,9 @@ static struct wpabuf *wpas_pasn_get_wrapped_data(struct wpas_pasn *pasn)
 
 static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
 {
+	if (pasn->using_pmksa)
+		return WPA_PASN_NO_WRAPPED_DATA;
+
 	/* Note: valid AKMP is expected to already be validated */
 	switch (pasn->akmp) {
 	case WPA_KEY_MGMT_SAE:
@@ -144,7 +337,7 @@ static struct wpabuf *wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
 		 * Note: even when PMKSA is available, also add wrapped data as
 		 * it is possible that the PMKID is no longer valid at the AP.
 		 */
-		wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+		wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
 	} else {
 		pmksa = NULL;
 	}
@@ -152,7 +345,6 @@ static struct wpabuf *wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
 	wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
 			  pasn->akmp, pasn->cipher);
 
-
 	if (!wrapped_data_buf)
 		wrapped_data = WPA_PASN_NO_WRAPPED_DATA;
 
@@ -212,7 +404,7 @@ static struct wpabuf *wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
 				   wpa_s->own_addr, pasn->bssid,
 				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
 
-	wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+	wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
 
 	if (!wrapped_data_buf)
 		wrapped_data = WPA_PASN_NO_WRAPPED_DATA;
@@ -276,6 +468,7 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
 	pasn->group = 0;
 	pasn->trans_seq = 0;
 	pasn->pmk_len = 0;
+	pasn->using_pmksa = 0;
 
 	os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
 	os_memset(&pasn->ptk, 0, sizeof(pasn->ptk));
@@ -284,6 +477,9 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
 	wpabuf_free(pasn->beacon_rsne);
 	pasn->beacon_rsne = NULL;
 
+#ifdef CONFIG_SAE
+	sae_clear_data(&pasn->sae);
+#endif /* CONFIG_SAE */
 	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 }
 
@@ -295,6 +491,7 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
 {
 	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
 	struct wpas_pasn *pasn = &wpa_s->pasn;
+	int ret;
 
 	os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
 	pasn->pmk_len = 0;
@@ -318,11 +515,32 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
 
 			pasn->pmk_len = pmksa->pmk_len;
 			os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
-
+			pasn->using_pmksa = 1;
 			return 0;
 		}
 	}
 
+#ifdef CONFIG_SAE
+	if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+		ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: failed processing SAE wrapped data");
+			pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "PASN: success deriving PMK with SAE");
+		pasn->pmk_len = PMK_LEN;
+		os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
+
+		wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
+					 pasn->pmk_len, pasn->sae.pmkid,
+					 pasn->bssid, pasn->akmp);
+		return 0;
+	}
+#endif /* CONFIG_SAE */
+
 	/* TODO: derive PMK based on wrapped data */
 	wpa_printf(MSG_DEBUG, "PASN: missing implementation to derive PMK");
 	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -332,9 +550,11 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
 
 static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
 			   int akmp, int cipher, u16 group, int freq,
-			   const u8 *beacon_rsne, u8 beacon_rsne_len)
+			   const u8 *beacon_rsne, u8 beacon_rsne_len,
+			   int network_id)
 {
 	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct wpa_ssid *ssid;
 	struct wpabuf *frame;
 	int ret;
 
@@ -345,11 +565,16 @@ static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		return -1;
 	}
 
+	ssid = wpa_config_get_network(wpa_s->conf, network_id);
+
 	switch (akmp) {
 	case WPA_KEY_MGMT_PASN:
 		break;
 #ifdef CONFIG_SAE
 	case WPA_KEY_MGMT_SAE:
+		pasn->sae.state = SAE_NOTHING;
+		pasn->sae.send_confirm = 0;
+		pasn->ssid = ssid;
 		break;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
@@ -503,7 +728,7 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
 
 	ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
 			      awork->group, bss->freq,
-			      rsne, *(rsne + 1) + 2);
+			      rsne, *(rsne + 1) + 2, awork->network_id);
 	if (ret) {
 		wpa_printf(MSG_DEBUG,
 			   "PASN: Failed to start PASN authentication");
@@ -521,7 +746,7 @@ fail:
 
 int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
 			 const u8 *bssid, int akmp, int cipher,
-			 u16 group)
+			 u16 group, int network_id)
 {
 	struct wpa_pasn_auth_work *awork;
 	struct wpa_bss *bss;
@@ -566,6 +791,7 @@ int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
 	awork->akmp = akmp;
 	awork->cipher = cipher;
 	awork->group = group;
+	awork->network_id = network_id;
 
 	if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1,
 			   wpas_pasn_auth_start_cb, awork) < 0) {
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 1313b5e025..f436b04c5e 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -3711,7 +3711,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
 	{ "pasn_auth_start", wpa_cli_cmd_pasn_auth_start, NULL,
 	  cli_cmd_flag_none,
 	  "bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> "
-          "= Start PASN authentication" },
+          "nid=<network id> = Start PASN authentication" },
 	{ "pasn_auth_stop", wpa_cli_cmd_pasn_auth_stop, NULL,
 	  cli_cmd_flag_none,
 	  "= Stop PASN authentication" },
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 932011acfa..dcf843eae7 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -509,12 +509,19 @@ struct wpas_pasn {
 	u8 bssid[ETH_ALEN];
 	u8 pmk_len;
 	u8 pmk[PMK_LEN_MAX];
+	u8 using_pmksa;
 
 	u8 hash[SHA384_MAC_LEN];
 
 	struct wpabuf *beacon_rsne;
 	struct wpa_ptk ptk;
 	struct crypto_ecdh *ecdh;
+
+#ifdef CONFIG_SAE
+	struct sae_data sae;
+#endif /* CONFIG_SAE */
+
+	const struct wpa_ssid *ssid;
 };
 
 #endif /* CONFIG_PASN */
@@ -1622,7 +1629,7 @@ int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
 void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s);
 int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
 			 const u8 *bssid, int akmp, int cipher,
-			 u16 group);
+			 u16 group, int network_id);
 void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s);
 int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
 			     const u8 *data, size_t data_len, u8 acked);
