wpa_supplicant: add support for bcmdhd SAE authentication offload

* Required to get WPA3 to work at all on bcmdhd devices

Change-Id: Ie96550256afd1596f26a377b4af104a8bd104c10
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 3c4de7a..5cbf88c 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1116,6 +1116,15 @@
 	 */
 	const u8 *psk;
 
+#ifdef CONFIG_BRCM_SAE
+	/**
+	 * sae_password - Password for SAE authentication
+	 *
+	 * This value is made available only for WPA3-Personal (SAE)
+	 */
+	const char *sae_password;
+#endif
+
 	/**
 	 * drop_unencrypted - Enable/disable unencrypted frame filtering
 	 *
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 1acc43b..79d1261 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -40,9 +40,9 @@
 #include "radiotap_iter.h"
 #include "rfkill.h"
 #include "driver_nl80211.h"
-#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) || defined(CONFIG_BRCM_SAE)
 #include "common/brcm_vendor.h"
-#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA || CONFIG_BRCM_SAE */
 
 #ifndef NETLINK_CAP_ACK
 #define NETLINK_CAP_ACK 10
@@ -3397,6 +3397,32 @@
 }
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
+#ifdef CONFIG_BRCM_SAE
+static int bcmdhd_set_sae_password(struct wpa_driver_nl80211_data *drv,
+				   const void *data, int len)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			BRCM_VENDOR_SCMD_BCM_PSK) ||
+	    nla_put(msg, NL80211_ATTR_VENDOR_DATA, len, data)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return -1;
+	}
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set SAE password failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+#endif /* CONFIG_BRCM_SAE */
 
 #if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
 static int key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
@@ -6935,6 +6961,23 @@
 			return -1;
 	}
 
+#ifdef CONFIG_BRCM_SAE
+	/* add SAE password in case of SAE authentication offload */
+	if ((params->sae_password || params->passphrase)) {
+		const char *password;
+		size_t pwd_len;
+
+		password = params->sae_password;
+		if (!password)
+			password = params->passphrase;
+		pwd_len = os_strlen(password);
+		wpa_hexdump_ascii_key(MSG_DEBUG, "  * SAE password",
+				      (u8 *) password, pwd_len);
+		if (bcmdhd_set_sae_password(drv, password, pwd_len))
+			return -1;
+	}
+#endif
+
 	if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
 		return -1;
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 16d6f5b..cc1ffda 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -19,6 +19,11 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "driver_nl80211.h"
+#ifdef CONFIG_BRCM_SAE
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_i.h"
+#include "wpa_supplicant_i.h"
+#endif
 
 static void
 nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
@@ -3139,6 +3144,33 @@
 	wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
 }
 
+#ifdef CONFIG_BRCM_SAE
+static void brcm_nl80211_sae_key_event(struct wpa_driver_nl80211_data *drv,
+                                       const u8 *data, size_t len)
+{
+	struct nlattr *tb[BRCM_SAE_KEY_ATTR_PMKID + 1];
+	struct wpa_pmkid_params params;
+	struct wpa_supplicant *wpa_s = drv->ctx;
+	struct wpa_sm *sm = wpa_s->wpa;
+
+	wpa_printf(MSG_DEBUG, "nl80211: BRCM SAE key vendor event received");
+
+	if (nla_parse(tb, BRCM_SAE_KEY_ATTR_PMKID, (struct nlattr *) data,
+			len, NULL))
+		return;
+
+	params.bssid = (u8*) nla_data(tb[BRCM_SAE_KEY_ATTR_BSSID]);
+	params.pmk = (u8*) nla_data(tb[BRCM_SAE_KEY_ATTR_PMK]);
+	params.pmk_len = nla_len(tb[BRCM_SAE_KEY_ATTR_PMK]);
+	params.pmkid = (u8*) nla_data(tb[BRCM_SAE_KEY_ATTR_PMKID]);
+
+	wpa_hexdump(MSG_DEBUG, "nl80211: Received BRCM SAE key for", params.bssid,
+			nla_len(tb[BRCM_SAE_KEY_ATTR_BSSID]));
+	wpa_printf(MSG_DEBUG, "nl80211: BRCM SAE key len=%zu", params.pmk_len);
+
+	wpa_sm_set_pmk(sm, params.pmk, params.pmk_len, params.pmkid, params.bssid);
+}
+#endif
 
 static void nl80211_vendor_event_brcm(struct wpa_driver_nl80211_data *drv,
 				      u32 subcmd, u8 *data, size_t len)
@@ -3153,6 +3185,11 @@
 	case BRCM_VENDOR_EVENT_ACS:
 		brcm_nl80211_acs_select_ch(drv, data, len);
 		break;
+#ifdef CONFIG_BRCM_SAE
+	case BRCM_VENDOR_EVENT_SAE_KEY:
+		brcm_nl80211_sae_key_event(drv, data, len);
+		break;
+#endif
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "%s: Ignore unsupported BRCM vendor event %u",
@@ -3214,11 +3251,11 @@
 	case OUI_QCA:
 		nl80211_vendor_event_qca(drv, subcmd, data, len);
 		break;
-#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
+#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA) || defined(CONFIG_BRCM_SAE)
 	case OUI_BRCM:
 		nl80211_vendor_event_brcm(drv, subcmd, data, len);
 		break;
-#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA */
+#endif /* CONFIG_DRIVER_NL80211_BRCM || CONFIG_DRIVER_NL80211_SYNA || CONFIG_BRCM_SAE */
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
 		break;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 474873a..9efa54a 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -18,6 +18,10 @@
   CONFIG_DRIVER_NL80211_QCA=y
 endif
 
+ifneq ($(BOARD_WLAN_BCMDHD_SAE),)
+  CONFIG_BRCM_SAE=y
+endif
+
 include $(LOCAL_PATH)/android.config
 
 # To ignore possible wrong network configurations
@@ -75,6 +79,11 @@
 L_CFLAGS += -DENABLE_PRIV_CMD_UPDATE_MBO_CELL_STATUS
 endif
 
+# BCMDHD SAE authentication offload
+ifdef CONFIG_BRCM_SAE
+L_CFLAGS += -DCONFIG_BRCM_SAE
+endif
+
 # Use Android specific directory for control interface sockets
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/vendor/wifi/wpa/sockets\"
 L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/vendor/wifi/wpa/sockets\"
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index a3645d1..32e14da 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -4291,6 +4291,19 @@
 			params.psk = psk;
 	}
 
+#ifdef CONFIG_BRCM_SAE
+	if (wpa_key_mgmt_sae(params.key_mgmt_suite)) {
+		params.auth_alg = WPA_AUTH_ALG_SAE;
+		if (ssid->sae_password)
+			params.sae_password = ssid->sae_password;
+		else if (ssid->passphrase)
+			params.passphrase = ssid->passphrase;
+
+		if (ssid->psk_set)
+			params.psk = ssid->psk;
+	}
+#endif
+
 	params.drop_unencrypted = use_crypt;
 
 	params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);