Commit 3b1e0a7b authored by Chung-Hsien Hsu's avatar Chung-Hsien Hsu Committed by Kalle Valo
Browse files

brcmfmac: add support for SAE authentication offload



The firmware may have SAE authentication code built-in. This is
detected by the driver and indicated in the wiphy features flags.
User-space can use this flag to determine whether or not to provide
the password material for SAE authentication in the nl80211 CONNECT
command.

Signed-off-by: default avatarChung-Hsien Hsu <stanley.hsu@cypress.com>
Signed-off-by: default avatarChi-Hsien Lin <chi-hsien.lin@cypress.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent f4268729
Loading
Loading
Loading
Loading
+68 −5
Original line number Diff line number Diff line
@@ -1282,6 +1282,31 @@ static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
	return err;
}

static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data,
				  u16 pwd_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;
}

static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
{
	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
@@ -1505,6 +1530,8 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev,
		val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
		val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
		val = WPA3_AUTH_SAE_PSK;
	else
		val = WPA_AUTH_DISABLED;
	brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
@@ -1537,6 +1564,10 @@ static s32 brcmf_set_auth_type(struct net_device *ndev,
		val = 1;
		brcmf_dbg(CONN, "shared key\n");
		break;
	case NL80211_AUTHTYPE_SAE:
		val = 3;
		brcmf_dbg(CONN, "SAE authentication\n");
		break;
	default:
		val = 2;
		brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
@@ -1705,6 +1736,16 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
				 sme->crypto.cipher_group);
			return -EINVAL;
		}
	} else if (val & WPA3_AUTH_SAE_PSK) {
		switch (sme->crypto.akm_suites[0]) {
		case WLAN_AKM_SUITE_SAE:
			val = WPA3_AUTH_SAE_PSK;
			break;
		default:
			bphy_err(drvr, "invalid cipher group (%d)\n",
				 sme->crypto.cipher_group);
			return -EINVAL;
		}
	}

	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
@@ -1776,7 +1817,8 @@ brcmf_set_sharedkey(struct net_device *ndev,
	brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
		  sec->wpa_versions, sec->cipher_pairwise);

	if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
	if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
				 NL80211_WPA_VERSION_3))
		return 0;

	if (!(sec->cipher_pairwise &
@@ -1983,7 +2025,13 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
		goto done;
	}

	if (sme->crypto.psk) {
	if (sme->crypto.sae_pwd) {
		brcmf_dbg(INFO, "using SAE offload\n");
		profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
	}

	if (sme->crypto.psk &&
	    profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
		if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
			err = -EINVAL;
			goto done;
@@ -2001,12 +2049,23 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
		}
	}

	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) {
	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
		err = brcmf_set_pmk(ifp, sme->crypto.psk,
				    BRCMF_WSEC_MAX_PSK_LEN);
		if (err)
	else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
		/* clean up user-space RSNE */
		if (brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0)) {
			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);
		if (!err && sme->crypto.psk)
			err = brcmf_set_pmk(ifp, sme->crypto.psk,
					    BRCMF_WSEC_MAX_PSK_LEN);
	}
	if (err)
		goto done;

	/* Join with specific BSSID and cached SSID
	 * If SSID is zero join based on BSSID only
@@ -5362,7 +5421,8 @@ static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
	if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
		brcmf_dbg(CONN, "Processing set ssid\n");
		memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
		if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK)
		if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
		    vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
			return true;

		set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
@@ -6672,6 +6732,9 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
		wiphy_ext_feature_set(wiphy,
				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
			wiphy_ext_feature_set(wiphy,
					      NL80211_EXT_FEATURE_SAE_OFFLOAD);
	}
	wiphy->mgmt_stypes = brcmf_txrx_stypes;
	wiphy->max_remain_on_channel_duration = 5000;
+2 −1
Original line number Diff line number Diff line
@@ -107,7 +107,8 @@ struct brcmf_cfg80211_security {
enum brcmf_profile_fwsup {
	BRCMF_PROFILE_FWSUP_NONE,
	BRCMF_PROFILE_FWSUP_PSK,
	BRCMF_PROFILE_FWSUP_1X
	BRCMF_PROFILE_FWSUP_1X,
	BRCMF_PROFILE_FWSUP_SAE
};

/**
+2 −1
Original line number Diff line number Diff line
@@ -39,7 +39,8 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
	{ BRCMF_FEAT_P2P, "p2p" },
	{ BRCMF_FEAT_MONITOR, "monitor" },
	{ BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
	{ BRCMF_FEAT_DOT11H, "802.11h" }
	{ BRCMF_FEAT_DOT11H, "802.11h" },
	{ BRCMF_FEAT_SAE, "sae" },
};

#ifdef DEBUG
+3 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
 * MONITOR_FMT_RADIOTAP: firmware provides monitor packets with radiotap header
 * MONITOR_FMT_HW_RX_HDR: firmware provides monitor packets with hw/ucode header
 * DOT11H: firmware supports 802.11h
 * SAE: simultaneous authentication of equals
 */
#define BRCMF_FEAT_LIST \
	BRCMF_FEAT_DEF(MBSS) \
@@ -45,7 +46,8 @@
	BRCMF_FEAT_DEF(MONITOR) \
	BRCMF_FEAT_DEF(MONITOR_FMT_RADIOTAP) \
	BRCMF_FEAT_DEF(MONITOR_FMT_HW_RX_HDR) \
	BRCMF_FEAT_DEF(DOT11H)
	BRCMF_FEAT_DEF(DOT11H) \
	BRCMF_FEAT_DEF(SAE)

/*
 * Quirks:
+13 −0
Original line number Diff line number Diff line
@@ -61,6 +61,8 @@
#define BRCMF_WSEC_MAX_PSK_LEN		32
#define	BRCMF_WSEC_PASSPHRASE		BIT(0)

#define BRCMF_WSEC_MAX_SAE_PASSWORD_LEN 128

/* primary (ie tx) key */
#define BRCMF_PRIMARY_KEY		(1 << 1)
#define DOT11_BSSTYPE_ANY		2
@@ -518,6 +520,17 @@ struct brcmf_wsec_pmk_le {
	u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1];
};

/**
 * struct brcmf_wsec_sae_pwd_le - firmware SAE password material.
 *
 * @key_len: number of octets in key materials.
 * @key: SAE password material.
 */
struct brcmf_wsec_sae_pwd_le {
	__le16 key_len;
	u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN];
};

/* Used to get specific STA parameters */
struct brcmf_scb_val_le {
	__le32 val;
Loading