wifi: brcmfmac: cfg80211: Use WSEC to set SAE password

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
This commit is contained in:
Hector Martin 2024-01-03 10:57:04 +01:00 committed by Kalle Valo
parent ba4d472633
commit 9f7861c56b
7 changed files with 76 additions and 35 deletions

View 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);
}

View 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 */

View 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,
};

View 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,

View 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];
};
/**

View 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_ */

View 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,
};