wifi: brcmfmac: cfg80211: Use WSEC to set SAE password
authorHector Martin <marcan@marcan.st>
Wed, 3 Jan 2024 09:57:04 +0000 (10:57 +0100)
committerKalle Valo <kvalo@kernel.org>
Fri, 19 Jan 2024 17:28:27 +0000 (19:28 +0200)
Using the WSEC command instead of sae_password seems to be the supported
mechanism on newer firmware, and also how the brcmdhd driver does it.

The existing firmware mechanism intended for (some) Cypress chips has
been separated from the new firmware mechanism using the multi-vendor
framework. Depending on the device it will select the appropriate
firmware mechanism.

This makes WPA3 work with iwd, or with wpa_supplicant pending a support
patchset [2].

[1] https://rachelbythebay.com/w/2023/11/06/wpa3/
[2] http://lists.infradead.org/pipermail/hostap/2023-July/041653.html

Signed-off-by: Hector Martin <marcan@marcan.st>
Reviewed-by: Neal Gompa <neal@gompa.dev>
[arend.vanspriel@broadcom.com: use multi-vendor framework]
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@kernel.org>
Link: https://msgid.link/20240103095704.135651-5-arend.vanspriel@broadcom.com
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/core.c

index 21096cff0c72f02c041e79f65688a7555a437c3f..736b2ada6f597af8e4a82eb86eec77a2b99e31a9 100644 (file)
@@ -32,6 +32,7 @@
 #include "vendor.h"
 #include "bus.h"
 #include "common.h"
+#include "fwvid.h"
 
 #define BRCMF_SCAN_IE_LEN_MAX          2048
 
@@ -1686,52 +1687,39 @@ static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
        return reason;
 }
 
-static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
+int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)
 {
        struct brcmf_pub *drvr = ifp->drvr;
        struct brcmf_wsec_pmk_le pmk;
        int err;
 
+       if (key_len > sizeof(pmk.key)) {
+               bphy_err(drvr, "key must be less than %zu bytes\n",
+                        sizeof(pmk.key));
+               return -EINVAL;
+       }
+
        memset(&pmk, 0, sizeof(pmk));
 
-       /* pass pmk directly */
-       pmk.key_len = cpu_to_le16(pmk_len);
-       pmk.flags = cpu_to_le16(0);
-       memcpy(pmk.key, pmk_data, pmk_len);
+       /* pass key material directly */
+       pmk.key_len = cpu_to_le16(key_len);
+       pmk.flags = cpu_to_le16(flags);
+       memcpy(pmk.key, key, key_len);
 
-       /* store psk in firmware */
+       /* store key material in firmware */
        err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
                                     &pmk, sizeof(pmk));
        if (err < 0)
                bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
-                        pmk_len);
+                        key_len);
 
        return err;
 }
+BRCMF_EXPORT_SYMBOL_GPL(brcmf_set_wsec);
 
-static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data,
-                                 u16 pwd_len)
+static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
 {
-       struct brcmf_pub *drvr = ifp->drvr;
-       struct brcmf_wsec_sae_pwd_le sae_pwd;
-       int err;
-
-       if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) {
-               bphy_err(drvr, "sae_password must be less than %d\n",
-                        BRCMF_WSEC_MAX_SAE_PASSWORD_LEN);
-               return -EINVAL;
-       }
-
-       sae_pwd.key_len = cpu_to_le16(pwd_len);
-       memcpy(sae_pwd.key, pwd_data, pwd_len);
-
-       err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd,
-                                      sizeof(sae_pwd));
-       if (err < 0)
-               bphy_err(drvr, "failed to set SAE password in firmware (len=%u)\n",
-                        pwd_len);
-
-       return err;
+       return brcmf_set_wsec(ifp, pmk_data, pmk_len, 0);
 }
 
 static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
@@ -2502,8 +2490,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
                        bphy_err(drvr, "failed to clean up user-space RSNE\n");
                        goto done;
                }
-               err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd,
-                                            sme->crypto.sae_pwd_len);
+               err = brcmf_fwvid_set_sae_password(ifp, &sme->crypto);
                if (!err && sme->crypto.psk)
                        err = brcmf_set_pmk(ifp, sme->crypto.psk,
                                            BRCMF_WSEC_MAX_PSK_LEN);
@@ -5251,8 +5238,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
                if (crypto->sae_pwd) {
                        brcmf_dbg(INFO, "using SAE offload\n");
                        profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE);
-                       err = brcmf_set_sae_password(ifp, crypto->sae_pwd,
-                                                    crypto->sae_pwd_len);
+                       err = brcmf_fwvid_set_sae_password(ifp, crypto);
                        if (err < 0)
                                goto exit;
                }
@@ -5359,10 +5345,12 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,
                msleep(400);
 
                if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) {
+                       struct cfg80211_crypto_settings crypto = {};
+
                        if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK))
                                brcmf_set_pmk(ifp, NULL, 0);
                        if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE))
-                               brcmf_set_sae_password(ifp, NULL, 0);
+                               brcmf_fwvid_set_sae_password(ifp, &crypto);
                        profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
                }
 
index 0e1fa3f0dea2cad2803b4934c0f94a1b9d0bb897..dc3a6a537507d1acb9f7bc97277e2e11925e0f10 100644 (file)
@@ -468,4 +468,6 @@ void brcmf_set_mpc(struct brcmf_if *ndev, int mpc);
 void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
 void brcmf_cfg80211_free_netdev(struct net_device *ndev);
 
+int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags);
+
 #endif /* BRCMFMAC_CFG80211_H */
index b75652ba9359f2a7b875fa995b09e70aafc6914c..24670497f1a40f08eb3473613356ddfa16dadfc7 100644 (file)
@@ -7,6 +7,7 @@
 #include <core.h>
 #include <bus.h>
 #include <fwvid.h>
+#include <fwil.h>
 
 #include "vops.h"
 
@@ -21,7 +22,34 @@ static void brcmf_cyw_detach(struct brcmf_pub *drvr)
        pr_err("%s: executing\n", __func__);
 }
 
+static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
+                                struct cfg80211_crypto_settings *crypto)
+{
+       struct brcmf_pub *drvr = ifp->drvr;
+       struct brcmf_wsec_sae_pwd_le sae_pwd;
+       u16 pwd_len = crypto->sae_pwd_len;
+       int err;
+
+       if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) {
+               bphy_err(drvr, "sae_password must be less than %d\n",
+                        BRCMF_WSEC_MAX_SAE_PASSWORD_LEN);
+               return -EINVAL;
+       }
+
+       sae_pwd.key_len = cpu_to_le16(pwd_len);
+       memcpy(sae_pwd.key, crypto->sae_pwd, pwd_len);
+
+       err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd,
+                                      sizeof(sae_pwd));
+       if (err < 0)
+               bphy_err(drvr, "failed to set SAE password in firmware (len=%u)\n",
+                        pwd_len);
+
+       return err;
+}
+
 const struct brcmf_fwvid_ops brcmf_cyw_ops = {
        .attach = brcmf_cyw_attach,
        .detach = brcmf_cyw_detach,
+       .set_sae_password = brcmf_cyw_set_sae_pwd,
 };
index 2aec7d2abd52566aba213eaaa9efd293792f303b..bc1c6b5a6e316cf29646b22369b4e549cdbbf3de 100644 (file)
@@ -211,6 +211,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, const char *name, const void *dat
        mutex_unlock(&drvr->proto_block);
        return err;
 }
+BRCMF_EXPORT_SYMBOL_GPL(brcmf_fil_iovar_data_set);
 
 s32
 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, const char *name, void *data,
index 9d248ba1c0b2bca1d48f39bd942baa17ea056ef8..e74a23e11830c171d8acbac252c5b6e056b2cfb1 100644 (file)
@@ -584,7 +584,7 @@ struct brcmf_wsec_key_le {
 struct brcmf_wsec_pmk_le {
        __le16  key_len;
        __le16  flags;
-       u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1];
+       u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN];
 };
 
 /**
index 17fbdbb76f51b729ed22d0ca2f295a66d3568042..d9fc76b46db963c7d425a858f5b4ebed24803ffa 100644 (file)
@@ -6,6 +6,7 @@
 #define FWVID_H_
 
 #include "firmware.h"
+#include "cfg80211.h"
 
 struct brcmf_pub;
 struct brcmf_if;
@@ -14,6 +15,7 @@ struct brcmf_fwvid_ops {
        int (*attach)(struct brcmf_pub *drvr);
        void (*detach)(struct brcmf_pub *drvr);
        void (*feat_attach)(struct brcmf_if *ifp);
+       int (*set_sae_password)(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto);
 };
 
 /* exported functions */
@@ -56,4 +58,15 @@ static inline void brcmf_fwvid_feat_attach(struct brcmf_if *ifp)
        vops->feat_attach(ifp);
 }
 
+static inline int brcmf_fwvid_set_sae_password(struct brcmf_if *ifp,
+                                              struct cfg80211_crypto_settings *crypto)
+{
+       const struct brcmf_fwvid_ops *vops = ifp->drvr->vops;
+
+       if (!vops || !vops->set_sae_password)
+               return -EOPNOTSUPP;
+
+       return vops->set_sae_password(ifp, crypto);
+}
+
 #endif /* FWVID_H_ */
index 5573a47766ad5f74dec03af406a0726e84e89b93..2d8f80bd73829f6ee94805a7aa055473a2a41fe4 100644 (file)
@@ -7,6 +7,7 @@
 #include <core.h>
 #include <bus.h>
 #include <fwvid.h>
+#include <fwil.h>
 
 #include "vops.h"
 
@@ -21,7 +22,15 @@ static void brcmf_wcc_detach(struct brcmf_pub *drvr)
        pr_debug("%s: executing\n", __func__);
 }
 
+static int brcmf_wcc_set_sae_pwd(struct brcmf_if *ifp,
+                                struct cfg80211_crypto_settings *crypto)
+{
+       return brcmf_set_wsec(ifp, crypto->sae_pwd, crypto->sae_pwd_len,
+                             BRCMF_WSEC_PASSPHRASE);
+}
+
 const struct brcmf_fwvid_ops brcmf_wcc_ops = {
        .attach = brcmf_wcc_attach,
        .detach = brcmf_wcc_detach,
+       .set_sae_password = brcmf_wcc_set_sae_pwd,
 };