[PATCH v2 15/20] PASN: Add support for PASN processing to the wpa_supplicant

Ilan Peer ilan.peer at intel.com
Wed Dec 16 06:00:28 EST 2020


Add PASN implementation to the wpa_supplicant

1. Add functions to initialize and clear PASN data.
2. Add functions to construct PASN authentication frames.
3. Add function to process PASN authentication frame.
4. Add function to handle PASN frame Tx status
5. Implement the station side flow processing for PASN

The implementation is missing support for wrapped data
and PMKSA establishment for base AKMs, and only supports
PASN authentication or base AKM with PMKSA caching.

The missing parts will be added in later patches.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/common/wpa_common.c           |   2 +
 src/common/wpa_ctrl.h             |   3 +
 wpa_supplicant/Makefile           |   3 +-
 wpa_supplicant/ctrl_iface.c       |   9 +
 wpa_supplicant/events.c           |  16 +
 wpa_supplicant/pasn_supplicant.c  | 893 ++++++++++++++++++++++++++++++
 wpa_supplicant/wpa_supplicant.c   |   8 +
 wpa_supplicant/wpa_supplicant_i.h |  41 ++
 8 files changed, 974 insertions(+), 1 deletion(-)
 create mode 100644 wpa_supplicant/pasn_supplicant.c

diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 7031208189..6300d774b4 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -2395,6 +2395,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 acc2d6c4c1..0f4208518e 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -408,6 +408,9 @@ extern "C" {
 #define BIT(x) (1U << (x))
 #endif
 
+/* 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 9abc2ec88c..a4a5045277 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -387,12 +387,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 39e8d9246d..da0a904df5 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,
@@ -8533,6 +8537,11 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
 	free_bss_tmp_disallowed(wpa_s);
 
 	os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
+
+#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 a9cc97e3bd..a60fda09fd 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4869,6 +4869,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
@@ -5097,6 +5106,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 ee76bc2212..473ef2fdfd 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -580,6 +580,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);
@@ -729,6 +733,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 ac8a79e485..510a6c2c9a 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
@@ -514,6 +518,30 @@ struct robust_av_data {
 	bool valid_config;
 };
 
+#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
  *
@@ -1333,6 +1361,11 @@ struct wpa_supplicant {
 	unsigned int multi_ap_fronthaul:1;
 	struct robust_av_data robust_av;
 	bool mscs_setup_done;
+
+#ifdef CONFIG_PASN
+	struct wpas_pasn pasn;
+	struct wpa_radio_work *pasn_auth_work;
+#endif /* CONFIG_PASN */
 };
 
 
@@ -1644,6 +1677,14 @@ 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);
 
 int wpas_send_mscs_req(struct wpa_supplicant *wpa_s);
 void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
-- 
2.17.1




More information about the Hostap mailing list