brcmfmac: add support for SAE authentication offload
authorChung-Hsien Hsu <stanley.hsu@cypress.com>
Wed, 2 Oct 2019 09:31:12 +0000 (09:31 +0000)
committerKalle Valo <kvalo@codeaurora.org>
Fri, 4 Oct 2019 13:45:51 +0000 (16:45 +0300)
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: Chung-Hsien Hsu <stanley.hsu@cypress.com>
Signed-off-by: Chi-Hsien Lin <chi-hsien.lin@cypress.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h

index 0cf4c79..5598bbd 100644 (file)
@@ -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;
index e29846b..6ce48f6 100644 (file)
@@ -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
 };
 
 /**
index 2c3526a..1c9c74c 100644 (file)
@@ -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
index 736a817..280a1f6 100644 (file)
@@ -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:
index 37c5120..de0ef1b 100644 (file)
@@ -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;
index 7b31c21..7552bdb 100644 (file)
@@ -231,6 +231,8 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec)
 #define WPA2_AUTH_FT           0x4000  /* Fast BSS Transition */
 #define WPA2_AUTH_PSK_SHA256   0x8000  /* PSK with SHA256 key derivation */
 
+#define WPA3_AUTH_SAE_PSK      0x40000 /* SAE with 4-way handshake */
+
 #define DOT11_DEFAULT_RTS_LEN          2347
 #define DOT11_DEFAULT_FRAG_LEN         2346