diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 485b71bd87..a30007c874 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -2382,6 +2382,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
 		return "OWE";
 	case WPA_KEY_MGMT_DPP:
 		return "DPP";
+	case WPA_KEY_MGMT_PASN:
+		return "PASN";
 	default:
 		return "UNKNOWN";
 	}
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 48002be6ce..c55c68a8d6 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -380,6 +380,9 @@ extern "C" {
 #define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED "
 #define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED "
 
+/* PASN authentication status */
+#define PASN_AUTH_STATUS "PASN-AUTH-STATUS "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL		0xFFFDFFFF
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 30c31bd18f..80615bfd90 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -388,12 +388,13 @@ endif
 
 ifdef CONFIG_PASN
 CFLAGS += -DCONFIG_PASN
+CFLAGS += -DCONFIG_PTKSA_CACHE
 NEED_HMAC_SHA256_KDF=y
 NEED_HMAC_SHA384_KDF=y
 NEED_SHA256=y
 NEED_SHA384=y
 OBJS += ../src/common/ptksa_cache.o
-CFLAGS += -DCONFIG_PTKSA_CACHE
+OBJS += pasn_supplicant.o
 endif
 
 ifdef CONFIG_WIFI_DISPLAY
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b9a0d206e9..9ed3c8b4a9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -64,6 +64,10 @@
 #include <net/ethernet.h>
 #endif
 
+#ifdef CONFIG_PASN
+#include "wpas_glue.h"
+#endif /* CONFIG_PASN */
+
 static int wpa_supplicant_global_iface_list(struct wpa_global *global,
 					    char *buf, int len);
 static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
@@ -8216,6 +8220,11 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
 	wpa_supplicant_update_channel_list(wpa_s, NULL);
 
 	free_bss_tmp_disallowed(wpa_s);
+
+#ifdef CONFIG_PASN
+	wpas_pasn_auth_stop(wpa_s);
+#endif /* CONFIG_PASN */
+
 }
 
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 5b09ec54e4..aa47366a10 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4578,6 +4578,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			" type=%d stype=%d",
 			MAC2STR(data->tx_status.dst),
 			data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_PASN
+		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+		    data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
+		    (wpas_pasn_auth_tx_status(wpa_s,
+					      data->tx_status.data,
+					      data->tx_status.data_len,
+					      data->tx_status.ack) == 0))
+			break;
+#endif /* CONFIG_PASN */
 #ifdef CONFIG_AP
 		if (wpa_s->ap_iface == NULL) {
 #ifdef CONFIG_OFFCHANNEL
@@ -4804,6 +4813,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 				mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
 				break;
 			}
+#ifdef CONFIG_PASN
+			if (stype == WLAN_FC_STYPE_AUTH) {
+				wpas_pasn_auth_rx(wpa_s, mgmt,
+						  data->rx_mgmt.frame_len);
+				break;
+			}
+#endif /* CONFIG_PASN*/
 
 #ifdef CONFIG_SAE
 			if (stype == WLAN_FC_STYPE_AUTH &&
diff --git a/wpa_supplicant/pasn_supplicant.c b/wpa_supplicant/pasn_supplicant.c
new file mode 100644
index 0000000000..2f92024b50
--- /dev/null
+++ b/wpa_supplicant/pasn_supplicant.c
@@ -0,0 +1,893 @@
+/*
+ * WPA Supplicant - PASN processing
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/dragonfly.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "crypto/crypto.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "driver_i.h"
+#include "bss.h"
+#include "common/ptksa_cache.h"
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct wpa_pasn_auth_work {
+	u8 bssid[ETH_ALEN];
+	int akmp;
+	int cipher;
+	u16 group;
+};
+
+
+static void wpas_pasn_auth_work_timeout(void *eloop_ctx,
+					void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "PASN: auth work timeout. stopping auth");
+
+	wpas_pasn_auth_stop(wpa_s);
+}
+
+
+static void wpas_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
+{
+	wpa_printf(MSG_DEBUG, "PASN: cancel pasn-start-auth work");
+
+	/* Remove pending/started work */
+	radio_remove_works(wpa_s, "pasn-start-auth", 0);
+}
+
+
+static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
+				  int akmp, int cipher, u8 status)
+{
+	wpa_msg(wpa_s, MSG_INFO,
+		PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
+		MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+		status);
+}
+
+
+static struct wpabuf *wpas_pasn_get_wrapped_data(struct wpas_pasn *pasn)
+{
+	switch (pasn->akmp) {
+	case WPA_KEY_MGMT_PASN:
+		/* no wrapped data */
+		return NULL;
+	case WPA_KEY_MGMT_SAE:
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+	default:
+		wpa_printf(MSG_ERROR,
+			   "PASN: TODO: Wrapped data for akmp=0x%x",
+			   pasn->akmp);
+		return NULL;
+	}
+}
+
+
+static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
+{
+	/* Note: valid AKMP is expected to already be validated */
+	switch (pasn->akmp) {
+	case WPA_KEY_MGMT_SAE:
+		return WPA_PASN_WRAPPED_DATA_SAE;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+		return WPA_PASN_WRAPPED_DATA_FILS_SK;
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		return WPA_PASN_WRAPPED_DATA_FT;
+	case WPA_KEY_MGMT_PASN:
+	default:
+		return WPA_PASN_NO_WRAPPED_DATA;
+	}
+}
+
+
+static struct wpabuf *wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+	struct rsn_pmksa_cache_entry *pmksa;
+	u8 wrapped_data;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: building frame 1");
+
+	if (pasn->trans_seq)
+		return NULL;
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	/* Get public key */
+	pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+	pubkey = wpabuf_zeropad(pubkey,
+				crypto_ecdh_prime_len(pasn->ecdh));
+	if (!pubkey) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to get pubkey");
+		goto fail;
+	}
+
+	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+	wpa_pasn_build_auth_header(buf, pasn->bssid,
+				   wpa_s->own_addr, pasn->bssid,
+				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+	if (wrapped_data != WPA_PASN_NO_WRAPPED_DATA) {
+		pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
+					       NULL, NULL,
+					       pasn->akmp);
+
+		/*
+		 * 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);
+	} else {
+		pmksa = NULL;
+	}
+
+	wpa_pasn_add_rsne(buf, pmksa ? pmksa->pmkid : NULL,
+			  pasn->akmp, pasn->cipher);
+
+
+	if (!wrapped_data_buf)
+		wrapped_data = WPA_PASN_NO_WRAPPED_DATA;
+
+	wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+				  pubkey, NULL, 0);
+
+	wpa_pasn_add_wrapped_data(buf, wrapped_data_buf);
+
+	ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+				   wpabuf_head(buf) + IEEE80211_HDRLEN,
+				   wpabuf_len(buf) - IEEE80211_HDRLEN,
+				   pasn->hash);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to compute hash");
+		goto fail;
+	}
+
+	pasn->trans_seq++;
+
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(pubkey);
+
+	wpa_printf(MSG_DEBUG, "PASN: frame 1: success");
+	return buf;
+fail:
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(pubkey);
+	wpabuf_free(buf);
+	return NULL;
+}
+
+
+static struct wpabuf *wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct wpabuf *buf, *wrapped_data_buf = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len, data_len;
+	const u8 *data;
+	u8 *ptr;
+	u8 wrapped_data;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
+
+	if (pasn->trans_seq != 2)
+		return NULL;
+
+	buf = wpabuf_alloc(1500);
+	if (!buf)
+		goto fail;
+
+	wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+	wpa_pasn_build_auth_header(buf, pasn->bssid,
+				   wpa_s->own_addr, pasn->bssid,
+				   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+	wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+
+	if (!wrapped_data_buf)
+		wrapped_data = WPA_PASN_NO_WRAPPED_DATA;
+
+	wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+				  NULL, NULL, 0);
+
+	wpa_pasn_add_wrapped_data(buf, wrapped_data_buf);
+	wpabuf_free(wrapped_data_buf);
+	wrapped_data_buf = NULL;
+
+	/* Add the mic */
+	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+	wpabuf_put_u8(buf, WLAN_EID_MIC);
+	wpabuf_put_u8(buf, mic_len);
+	ptr = wpabuf_put(buf, mic_len);
+
+	os_memset(ptr, 0, mic_len);
+
+	data = wpabuf_head(buf) + IEEE80211_HDRLEN;
+	data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+		       wpa_s->own_addr, pasn->bssid,
+		       pasn->hash, mic_len * 2,
+		       data, data_len,
+		       mic);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed mic calculation");
+		goto fail;
+	}
+
+	os_memcpy(ptr, mic, mic_len);
+
+	pasn->trans_seq++;
+
+	wpa_printf(MSG_DEBUG, "PASN: frame 3: success");
+	return buf;
+fail:
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	wpabuf_free(wrapped_data_buf);
+	wpabuf_free(buf);
+	return NULL;
+}
+
+
+static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+
+	wpa_printf(MSG_DEBUG, "PASN: reset");
+
+	crypto_ecdh_deinit(pasn->ecdh);
+
+	wpas_pasn_cancel_auth_work(wpa_s);
+	wpa_s->pasn_auth_work = NULL;
+
+	pasn->ecdh = NULL;
+	pasn->akmp = 0;
+	pasn->cipher = 0;
+	pasn->group = 0;
+	pasn->trans_seq = 0;
+	pasn->pmk_len = 0;
+
+	os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
+	os_memset(&pasn->ptk, 0, sizeof(pasn->ptk));
+	os_memset(&pasn->hash, 0, sizeof(pasn->hash));
+
+	wpabuf_free(pasn->beacon_rsne);
+	pasn->beacon_rsne = NULL;
+
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+}
+
+
+static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
+			     struct wpa_ie_data *rsn_data,
+			     struct wpa_pasn_params_data *pasn_data,
+			     struct wpabuf *wrapped_data)
+{
+	static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+
+	os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
+	pasn->pmk_len = 0;
+
+	if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+		wpa_printf(MSG_DEBUG, "PASN: using default PMK");
+
+		pasn->pmk_len = WPA_PASN_PMK_LEN;
+		os_memcpy(pasn->pmk, pasn_default_pmk,
+			  sizeof(pasn_default_pmk));
+		return 0;
+	}
+
+	if (rsn_data->num_pmkid) {
+		struct rsn_pmksa_cache_entry *pmksa =
+			wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
+					       rsn_data->pmkid, NULL,
+					       pasn->akmp);
+		if (pmksa) {
+			wpa_printf(MSG_DEBUG, "PASN: using PMKSA");
+
+			pasn->pmk_len = pmksa->pmk_len;
+			os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
+
+			return 0;
+		}
+	}
+
+	/* TODO: derive PMK based on wrapped data */
+	wpa_printf(MSG_DEBUG, "PASN: missing implementation to derive PMK");
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	return -1;
+}
+
+
+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)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct wpabuf *frame;
+	int ret;
+
+	/* TODO: Currently support only ECC groups */
+	if (!dragonfly_suitable_group(group, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Reject unsuitable group %u", group);
+		return -1;
+	}
+
+	switch (akmp) {
+	case WPA_KEY_MGMT_PASN:
+		break;
+#ifdef CONFIG_SAE
+	case WPA_KEY_MGMT_SAE:
+		break;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WPA_KEY_MGMT_FILS_SHA256:
+		break;
+	case WPA_KEY_MGMT_FILS_SHA384:
+		break;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+	case WPA_KEY_MGMT_FT_PSK:
+	case WPA_KEY_MGMT_FT_IEEE8021X:
+	case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+		break;
+#endif /* CONFIG_IEEE80211R */
+	default:
+		wpa_printf(MSG_ERROR, "PASN: unsupported AKMP=0x%x",
+			   akmp);
+		return -1;
+	}
+
+	pasn->ecdh = crypto_ecdh_init(group);
+	if (!pasn->ecdh) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed init ECDH");
+		goto fail;
+	}
+
+	pasn->beacon_rsne = wpabuf_alloc(beacon_rsne_len);
+	if (!pasn->beacon_rsne) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE");
+		goto fail;
+	}
+
+	wpabuf_put_data(pasn->beacon_rsne, beacon_rsne, beacon_rsne_len);
+
+	pasn->akmp = akmp;
+	pasn->cipher = cipher;
+	pasn->group = group;
+	pasn->freq = freq;
+	os_memcpy(pasn->bssid, bssid, ETH_ALEN);
+
+	wpa_printf(MSG_DEBUG,
+		   "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
+		   MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
+		   pasn->group);
+
+
+	frame = wpas_pasn_build_auth_1(wpa_s);
+	if (!frame) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
+		goto fail;
+	}
+
+	ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
+				pasn->freq, 1000);
+
+	wpabuf_free(frame);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+		goto fail;
+	}
+
+	eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout,
+			       wpa_s, NULL);
+	return 0;
+
+fail:
+	return -1;
+}
+
+
+static struct wpa_bss *wpas_pasn_allowed(struct wpa_supplicant *wpa_s,
+					 const u8 *bssid, int akmp, int cipher)
+{
+	struct wpa_bss *bss;
+	const u8 *rsne;
+	struct wpa_ie_data rsne_data;
+	int ret;
+
+	if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Not doing authentication with current BSS");
+		return NULL;
+	}
+
+	bss = wpa_bss_get_bssid(wpa_s, bssid);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "PASN: BSS not found");
+		return NULL;
+	}
+
+	rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsne) {
+		wpa_printf(MSG_DEBUG, "PASN: BSS without RSN IE");
+		return NULL;
+	}
+
+	ret = wpa_parse_wpa_ie(rsne, *(rsne + 1) + 2, &rsne_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data");
+		return NULL;
+	}
+
+	if (!(rsne_data.key_mgmt & akmp) ||
+	    !(rsne_data.pairwise_cipher & cipher)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: AP does not support requested AKMP or cipher");
+		return NULL;
+	}
+
+	return bss;
+}
+
+
+static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+	struct wpa_supplicant *wpa_s = work->wpa_s;
+	struct wpa_pasn_auth_work *awork = work->ctx;
+	struct wpa_bss *bss;
+	const u8 *rsne;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit);
+
+	if (deinit) {
+		if (work->started) {
+			eloop_cancel_timeout(wpas_pasn_auth_work_timeout,
+					     wpa_s, NULL);
+			wpa_s->pasn_auth_work = NULL;
+		}
+		os_free(awork);
+		return;
+	}
+
+	/*
+	 * It is possible that by the time the callback is called, the PASN
+	 * authentication is not allowed, e.g., a connection with the AP was
+	 * established
+	 */
+	bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp,
+				awork->cipher);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: not allowed");
+		goto fail;
+	}
+
+	rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	if (!rsne) {
+		wpa_printf(MSG_DEBUG, "PASN: BSS without RSN IE");
+		goto fail;
+	}
+
+	ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
+			      awork->group, bss->freq,
+			      rsne, *(rsne + 1) + 2);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Failed to start PASN authentication");
+		goto fail;
+	}
+
+	wpa_s->pasn_auth_work = work;
+	return;
+fail:
+	os_free(awork);
+	work->ctx = NULL;
+	radio_work_done(work);
+}
+
+
+int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
+			 const u8 *bssid, int akmp, int cipher,
+			 u16 group)
+{
+	struct wpa_pasn_auth_work *awork;
+	struct wpa_bss *bss;
+
+	wpa_printf(MSG_DEBUG, "PASN: start: " MACSTR " akmp=0x%x, cipher=0x%x",
+		   MAC2STR(bssid),
+		   akmp, cipher);
+
+	/*
+	 * TODO: Consider modifying the offchannel logic to handle additional
+	 * management frames other then action frames. For now allow PASN only
+	 * with drivers that support off-channel Tx.
+	 */
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Driver does not support offchannel Tx");
+		return -1;
+	}
+
+	if (radio_work_pending(wpa_s, "pasn-start-auth")) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: send_auth: working is already pending");
+		return -1;
+	}
+
+	if (wpa_s->pasn_auth_work) {
+		wpa_printf(MSG_DEBUG, "PASN: send_auth: already in progress");
+		return -1;
+	}
+
+	bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher);
+	if (!bss)
+		return -1;
+
+	wpas_pasn_reset(wpa_s);
+
+	awork = os_zalloc(sizeof(*awork));
+	if (!awork)
+		return -1;
+
+	os_memcpy(awork->bssid, bssid, ETH_ALEN);
+	awork->akmp = akmp;
+	awork->cipher = cipher;
+	awork->group = group;
+
+	if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1,
+			   wpas_pasn_auth_start_cb, awork) < 0) {
+		os_free(awork);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: auth work successfully added");
+	return 0;
+}
+
+
+void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+
+	if (!wpa_s->pasn.ecdh)
+		return;
+
+	wpa_printf(MSG_DEBUG, "PASN: stopping authentication");
+
+	wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
+			      pasn->status);
+
+	wpas_pasn_reset(wpa_s);
+}
+
+
+int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+		      const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn_data;
+	struct wpa_pasn_params_data pasn_params;
+	struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
+	u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+	u8 mic_len;
+	u16 status;
+	int ret;
+	u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+			      (WLAN_FC_STYPE_AUTH << 4));
+
+	if (!wpa_s->pasn_auth_work || !mgmt ||
+	    len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+		return 0;
+
+	/* Not an authentication frame; do nothing */
+	if ((mgmt->frame_control & fc) != fc)
+		return 0;
+
+	/* Not our frame; do nothing */
+	if (os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN) ||
+	    os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) ||
+	    os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
+		return 0;
+
+	/* Not PASN; do nothing */
+	if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
+		return 0;
+
+	if (mgmt->u.auth.auth_transaction !=
+	    host_to_le16(pasn->trans_seq + 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: Rx: invalid transaction sequence: (%u != %u)",
+			   pasn->trans_seq + 1,
+			   le_to_host16(mgmt->u.auth.auth_transaction));
+		return -1;
+	}
+
+	status = le_to_host16(mgmt->u.auth.status_code);
+
+	if (status != WLAN_STATUS_SUCCESS &&
+	    status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: authentication rejected. status=%u", status);
+
+		pasn->status = status;
+		wpas_pasn_auth_stop(wpa_s);
+		return -1;
+	}
+
+	if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+				   len - offsetof(struct ieee80211_mgmt,
+						  u.auth.variable),
+				   &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: failed parsing authentication frame");
+		goto fail;
+	}
+
+	/* check that the MIC IE exists. Save it and zero out the memory */
+	mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+	if (status == WLAN_STATUS_SUCCESS) {
+		if (!elems.mic || elems.mic_len != mic_len) {
+			wpa_printf(MSG_DEBUG,
+				   "PASN: invalid mic. Expecting len=%u",
+				   mic_len);
+			goto fail;
+		} else {
+			os_memcpy(mic, elems.mic, mic_len);
+			os_memset((u8 *)elems.mic, 0, mic_len);
+		}
+	}
+
+	if (!elems.pasn_params || !elems.pasn_params_len) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: missing pasn parameters IE");
+		goto fail;
+	}
+
+	ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+					  elems.pasn_params_len + 3,
+					  &pasn_params);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: failed validation pasn parameters IE");
+		goto fail;
+	}
+
+	/* TODO: handle comeback flow */
+	if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: authentication temporarily rejected");
+		goto fail;
+	}
+
+	ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+			       &rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed parsing RNSE");
+		goto fail;
+	}
+
+	ret = wpa_pasn_validate_rsne(&rsn_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed validating RSNE");
+		goto fail;
+	}
+
+	if (pasn->akmp != rsn_data.key_mgmt ||
+	    pasn->cipher != rsn_data.pairwise_cipher) {
+		wpa_printf(MSG_DEBUG, "PASN: mismatch in akmp/cipher");
+		goto fail;
+	}
+
+	if (pasn->group != pasn_params.group) {
+		wpa_printf(MSG_DEBUG, "PASN: mismatch in group");
+		goto fail;
+	}
+
+	if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
+		wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+		goto fail;
+	}
+
+	secret = crypto_ecdh_set_peerkey(pasn->ecdh, 0,
+					 pasn_params.pubkey,
+					 pasn_params.pubkey_len);
+
+	if (!secret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed to derive secret");
+		goto fail;
+	}
+
+	if (pasn_params.wrapped_data_format != WPA_PASN_NO_WRAPPED_DATA) {
+		wrapped_data = ieee802_11_defrag(&elems,
+						 WLAN_EID_EXTENSION,
+						 WLAN_EID_EXT_WRAPPED_DATA);
+
+		if (!wrapped_data) {
+			wpa_printf(MSG_DEBUG, "PASN: missing wrapped data");
+			goto fail;
+		}
+	}
+
+	ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to set PMK");
+		goto fail;
+	}
+
+	ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
+			      wpa_s->own_addr, pasn->bssid,
+			      wpabuf_head(secret), wpabuf_len(secret),
+			      &pasn->ptk, pasn->akmp, pasn->cipher,
+			      WPA_HLTK_MAX_LEN);
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: failed to derive PTK");
+		goto fail;
+	}
+
+	wpabuf_free(wrapped_data);
+	wrapped_data = NULL;
+	wpabuf_free(secret);
+	secret = NULL;
+
+	/* verify the MIC */
+	ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+		       pasn->bssid, wpa_s->own_addr,
+		       wpabuf_head(pasn->beacon_rsne),
+		       wpabuf_len(pasn->beacon_rsne),
+		       (u8 *)&mgmt->u.auth,
+		       len - offsetof(struct ieee80211_mgmt, u.auth),
+		       out_mic);
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: frame MIC:  ", mic, mic_len);
+	if (ret || os_memcmp(mic, out_mic, mic_len)) {
+		wpa_printf(MSG_DEBUG, "PASN: failed MIC verification");
+		goto fail;
+	}
+
+	pasn->trans_seq++;
+
+	wpa_printf(MSG_DEBUG, "PASN: Success verifying authentication frame");
+
+	frame = wpas_pasn_build_auth_3(wpa_s);
+	if (!frame) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
+		goto fail;
+	}
+
+	ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
+				pasn->freq, 100);
+
+	wpabuf_free(frame);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "PASN: success sending last frame. Store PTK");
+
+	ptksa_cache_add(wpa_s->ptksa, pasn->bssid, pasn->cipher,
+			dot11RSNAConfigPMKLifetime, &pasn->ptk);
+
+	os_memset(&pasn->ptk, 0, sizeof(pasn->ptk));
+
+	pasn->status = WLAN_STATUS_SUCCESS;
+	return 0;
+fail:
+	wpa_printf(MSG_DEBUG, "PASN: failed Rx processing. Terminating");
+	wpabuf_free(wrapped_data);
+	wpabuf_free(secret);
+
+	/*
+	 * TODO: In case of an error the specification allows to silently drop
+	 * the frame and terminate the authentication exchange. However, better
+	 * reply to the AP with an error status
+	 */
+	pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+	wpas_pasn_auth_stop(wpa_s);
+	return -1;
+}
+
+
+int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
+			     const u8 *data, size_t data_len, u8 acked)
+
+{
+	struct wpas_pasn *pasn = &wpa_s->pasn;
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)data;
+	u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+			      (WLAN_FC_STYPE_AUTH << 4));
+
+	wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+
+	if (!wpa_s->pasn_auth_work) {
+		wpa_printf(MSG_DEBUG,
+			   "PASN: auth_tx_status: no work in progress");
+		return -1;
+	}
+
+	if (!mgmt ||
+	    data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+		return -1;
+
+	/* Not an authentication frame; do nothing */
+	if ((mgmt->frame_control & fc) != fc)
+		return -1;
+
+	/* Not our frame; do nothing */
+	if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
+	    os_memcmp(mgmt->sa, wpa_s->own_addr, ETH_ALEN) ||
+	    os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
+		return -1;
+
+	/* Not PASN; do nothing */
+	if (mgmt->u.auth.auth_alg !=  host_to_le16(WLAN_AUTH_PASN))
+		return -1;
+
+	if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
+		wpa_printf(MSG_ERROR,
+			   "PASN: Invalid transaction sequence: (%u != %u)",
+			   pasn->trans_seq,
+			   le_to_host16(mgmt->u.auth.auth_transaction));
+		return 0;
+	}
+
+	wpa_printf(MSG_ERROR,
+		   "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
+		   acked);
+
+	/*
+	 * Even if the frame was not acked, do not treat this is an error, and
+	 * try to complete the flow, relying on the pasn timeout callback to
+	 * cleanup.
+	 */
+	if (pasn->trans_seq == 3) {
+		wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
+			   MAC2STR(pasn->bssid));
+		goto stop_auth;
+	}
+
+	return 0;
+
+stop_auth:
+	/*
+	 * Either frame was not acked or it was acked but the trans_seq != 1,
+	 * i.e., not expecting an Rx frame, so we are done.
+	 */
+	wpas_pasn_auth_stop(wpa_s);
+	return 0;
+}
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index d357633e63..d1102d9b4b 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -572,6 +572,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 	wpa_s->wpa = NULL;
 	wpa_blacklist_clear(wpa_s);
 
+#ifdef CONFIG_PASN
+	wpas_pasn_auth_stop(wpa_s);
+#endif /* CONFIG_PASN */
+
 	wpa_bss_deinit(wpa_s);
 
 	wpa_supplicant_cancel_delayed_sched_scan(wpa_s);
@@ -721,6 +725,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 	dpp_global_deinit(wpa_s->dpp);
 	wpa_s->dpp = NULL;
 #endif /* CONFIG_DPP */
+
+#ifdef CONFIG_PASN
+	wpas_pasn_auth_stop(wpa_s);
+#endif /* CONFIG_PASN */
 }
 
 
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index f4ffba2934..932011acfa 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -18,6 +18,10 @@
 #include "config_ssid.h"
 #include "wmm_ac.h"
 
+#ifdef CONFIG_PASN
+#include "crypto/sha384.h"
+#endif /* CONFIG_PASN */
+
 extern const char *const wpa_supplicant_version;
 extern const char *const wpa_supplicant_license;
 #ifndef CONFIG_NO_STDOUT_DEBUG
@@ -491,6 +495,30 @@ struct driver_signal_override {
 	int scan_level;
 };
 
+#ifdef CONFIG_PASN
+
+struct wpas_pasn {
+	int akmp;
+	int cipher;
+	u16 group;
+	int freq;
+
+	u8 trans_seq;
+	u8 status;
+
+	u8 bssid[ETH_ALEN];
+	u8 pmk_len;
+	u8 pmk[PMK_LEN_MAX];
+
+	u8 hash[SHA384_MAC_LEN];
+
+	struct wpabuf *beacon_rsne;
+	struct wpa_ptk ptk;
+	struct crypto_ecdh *ecdh;
+};
+
+#endif /* CONFIG_PASN */
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -1288,6 +1316,11 @@ struct wpa_supplicant {
 	unsigned int enabled_4addr_mode:1;
 	unsigned int multi_bss_support:1;
 	unsigned int drv_authorized_port:1;
+
+#ifdef CONFIG_PASN
+	struct wpas_pasn pasn;
+	struct wpa_radio_work *pasn_auth_work;
+#endif /* CONFIG_PASN */
 };
 
 
@@ -1587,5 +1620,13 @@ int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
 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);
+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);
+int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+		      const struct ieee80211_mgmt *mgmt, size_t len);
 
 #endif /* WPA_SUPPLICANT_I_H */
