[PATCH 01/14] PASN: Support PASN with SAE key derivation

Ilan Peer <[email protected]>
Newsgroups gmane.linux.drivers.hostap
Message-ID <[email protected]>
Signed-off-by: Ilan Peer <[email protected]>
---
 src/rsn_supp/wpa.c                |  11 ++
 src/rsn_supp/wpa.h                |   2 +
 wpa_supplicant/ctrl_iface.c       |   6 +-
 wpa_supplicant/pasn_supplicant.c  | 242 +++++++++++++++++++++++++++++-
 wpa_supplicant/wpa_cli.c          |   2 +-
 wpa_supplicant/wpa_supplicant_i.h |   9 +-
 6 files changed, 261 insertions(+), 11 deletions(-)

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);
-- 
2.17.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.