]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: Add FILS SK auth PFS support in STA mode
authorJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 20:45:35 +0000 (22:45 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 21:20:32 +0000 (23:20 +0200)
This adds an option to configure wpa_supplicant to use the perfect
forward secrecy option in FILS shared key authentication. A new build
option CONFIG_FILS_SK_PFS=y can be used to include this functionality. A
new runtime network profile parameter fils_dh_group is used to enable
this by specifying which DH group to use. For example, fils_dh_group=19
would use FILS SK PFS with a 256-bit random ECP group.

Signed-off-by: Jouni Malinen <j@w1.fi>
12 files changed:
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_i.h
wpa_supplicant/Android.mk
wpa_supplicant/Makefile
wpa_supplicant/config.c
wpa_supplicant/config_file.c
wpa_supplicant/config_ssid.h
wpa_supplicant/defconfig
wpa_supplicant/events.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.conf

index d3fd8ef50c2b9340ace2b9c2e748ea2c954c4b8e..5eb9834476446a64170fcd06e31c603926222445 100644 (file)
@@ -2461,6 +2461,9 @@ void wpa_sm_deinit(struct wpa_sm *sm)
 #ifdef CONFIG_TESTING_OPTIONS
        wpabuf_free(sm->test_assoc_ie);
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_FILS
+       crypto_ecdh_deinit(sm->fils_ecdh);
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_OWE
        crypto_ecdh_deinit(sm->owe_ecdh);
 #endif /* CONFIG_OWE */
@@ -3265,10 +3268,11 @@ void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
 
 #ifdef CONFIG_FILS
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group)
 {
        struct wpabuf *buf = NULL;
        struct wpabuf *erp_msg;
+       struct wpabuf *pub = NULL;
 
        erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
        if (!erp_msg && !sm->cur_pmksa) {
@@ -3296,7 +3300,28 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm)
        wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
                    sm->fils_session, FILS_SESSION_LEN);
 
-       buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+#ifdef CONFIG_FILS_SK_PFS
+       sm->fils_dh_group = dh_group;
+       if (dh_group) {
+               crypto_ecdh_deinit(sm->fils_ecdh);
+               sm->fils_ecdh = crypto_ecdh_init(dh_group);
+               if (!sm->fils_ecdh) {
+                       wpa_printf(MSG_INFO,
+                                  "FILS: Could not initialize ECDH with group %d",
+                                  dh_group);
+                       goto fail;
+               }
+               pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+               if (!pub)
+                       goto fail;
+               wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)",
+                               pub);
+               sm->fils_dh_elem_len = wpabuf_len(pub);
+       }
+#endif /* CONFIG_FILS_SK_PFS */
+
+       buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len +
+                          (pub ? wpabuf_len(pub) : 0));
        if (!buf)
                goto fail;
 
@@ -3308,8 +3333,15 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm)
        /* Status Code */
        wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 
-       /* TODO: Finite Cyclic Group when using PK or PFS */
-       /* TODO: Element when using PK or PFS */
+       /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+       if (dh_group) {
+               /* Finite Cyclic Group */
+               wpabuf_put_le16(buf, dh_group);
+               /* Element */
+               wpabuf_put_buf(buf, pub);
+       }
+#endif /* CONFIG_FILS_SK_PFS */
 
        /* RSNE */
        wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
@@ -3354,6 +3386,7 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm)
 
 fail:
        wpabuf_free(erp_msg);
+       wpabuf_free(pub);
        return buf;
 }
 
@@ -3368,6 +3401,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        u8 ick[FILS_ICK_MAX_LEN];
        size_t ick_len;
        int res;
+       struct wpabuf *dh_ss = NULL;
 
        os_memcpy(sm->bssid, bssid, ETH_ALEN);
 
@@ -3376,13 +3410,53 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        pos = data;
        end = data + len;
 
-       /* TODO: Finite Cyclic Group when using PK or PFS */
-       /* TODO: Element when using PK or PFS */
+       /* TODO: FILS PK */
+#ifdef CONFIG_FILS_SK_PFS
+       if (sm->fils_dh_group) {
+               u16 group;
+
+               /* Using FILS PFS */
+
+               /* Finite Cyclic Group */
+               if (end - pos < 2) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: No room for Finite Cyclic Group");
+                       goto fail;
+               }
+               group = WPA_GET_LE16(pos);
+               pos += 2;
+               if (group != sm->fils_dh_group) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)",
+                                  group, sm->fils_dh_group);
+                       goto fail;
+               }
+
+               /* Element */
+               if ((size_t) (end - pos) < sm->fils_dh_elem_len) {
+                       wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+                       goto fail;
+               }
+
+               if (!sm->fils_ecdh) {
+                       wpa_printf(MSG_DEBUG, "FILS: No ECDH state available");
+                       goto fail;
+               }
+               dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos,
+                                               sm->fils_dh_elem_len);
+               if (!dh_ss) {
+                       wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+                       goto fail;
+               }
+               wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss);
+               pos += sm->fils_dh_elem_len;
+       }
+#endif /* CONFIG_FILS_SK_PFS */
 
        wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
        if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
                wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
-               return -1;
+               goto fail;
        }
 
        /* RSNE */
@@ -3392,12 +3466,12 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
            wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
                                 &rsn) < 0) {
                wpa_printf(MSG_DEBUG, "FILS: No RSN element");
-               return -1;
+               goto fail;
        }
 
        if (!elems.fils_nonce) {
                wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
-               return -1;
+               goto fail;
        }
        os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
        wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
@@ -3412,7 +3486,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
 
                if (rsn.num_pmkid != 1) {
                        wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
-                       return -1;
+                       goto fail;
                }
                wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
                if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
@@ -3420,7 +3494,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                        wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
                        wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
                                    sm->cur_pmksa->pmkid, PMKID_LEN);
-                       return -1;
+                       goto fail;
                }
                wpa_printf(MSG_DEBUG,
                           "FILS: Matching PMKID - continue using PMKSA caching");
@@ -3435,7 +3509,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        /* FILS Session */
        if (!elems.fils_session) {
                wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
-               return -1;
+               goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
                    FILS_SESSION_LEN);
@@ -3444,7 +3518,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
                wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
                            sm->fils_session, FILS_SESSION_LEN);
-               return -1;
+               goto fail;
        }
 
        /* FILS Wrapped Data */
@@ -3458,7 +3532,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
                                            elems.fils_wrapped_data_len);
                if (eapol_sm_failed(sm->eapol))
-                       return -1;
+                       goto fail;
 
                rmsk_len = ERP_MAX_KEY_LEN;
                res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
@@ -3467,18 +3541,22 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                        res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
                }
                if (res)
-                       return -1;
+                       goto fail;
 
                res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
-                                      sm->fils_nonce, sm->fils_anonce, NULL, 0,
+                                      sm->fils_nonce, sm->fils_anonce,
+                                      dh_ss ? wpabuf_head(dh_ss) : NULL,
+                                      dh_ss ? wpabuf_len(dh_ss) : 0,
                                       sm->pmk, &sm->pmk_len);
                os_memset(rmsk, 0, sizeof(rmsk));
+               wpabuf_clear_free(dh_ss);
+               dh_ss = NULL;
                if (res)
-                       return -1;
+                       goto fail;
 
                if (!sm->fils_erp_pmkid_set) {
                        wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
-                       return -1;
+                       goto fail;
                }
                wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
                            PMKID_LEN);
@@ -3493,7 +3571,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        if (!sm->cur_pmksa) {
                wpa_printf(MSG_DEBUG,
                           "FILS: No remaining options to continue FILS authentication");
-               return -1;
+               goto fail;
        }
 
        if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
@@ -3501,7 +3579,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                            ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher) <
            0) {
                wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
-               return -1;
+               goto fail;
        }
        sm->ptk_set = 1;
        sm->tptk_set = 0;
@@ -3509,12 +3587,15 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
 
        res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
                               sm->fils_anonce, sm->own_addr, sm->bssid,
-                              NULL, 0, NULL, 0, /* TODO: SK+PFS */
+                              NULL, 0, NULL, 0, /* TODO: PK */
                               sm->key_mgmt, sm->fils_key_auth_sta,
                               sm->fils_key_auth_ap,
                               &sm->fils_key_auth_len);
        os_memset(ick, 0, sizeof(ick));
        return res;
+fail:
+       wpabuf_clear_free(dh_ss);
+       return -1;
 }
 
 
index df774ade82aae2c1e3df7571fe886dd4f3ff3cbb..cd9f56dca49a605dbf742543cf12e1aca1a15376 100644 (file)
@@ -435,7 +435,7 @@ extern unsigned int tdls_testing;
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm);
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group);
 int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                      size_t len);
 struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
index fe5311772f048f74f55a43c1aeb6dfdf1f90cb76..d1dccec5956d321e48537eb869d0c01ff92e43da 100644 (file)
@@ -151,6 +151,9 @@ struct wpa_sm {
        unsigned int fils_cache_id_set:1;
        u8 fils_erp_pmkid[PMKID_LEN];
        u8 fils_cache_id[FILS_CACHE_ID_LEN];
+       struct crypto_ecdh *fils_ecdh;
+       int fils_dh_group;
+       size_t fils_dh_elem_len;
 #endif /* CONFIG_FILS */
 
 #ifdef CONFIG_OWE
index bf6ad882d663c10d7707102c15cd0e447a7dde0b..79493e35fbfa6ea7051b6579d108658be6fe6baa 100644 (file)
@@ -253,6 +253,10 @@ ifdef CONFIG_FILS
 L_CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+L_CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
 endif
 
 ifdef CONFIG_WNM
index 968f7bc3a72d9abfa3c28de41c8fabf573efe3c9..95bb52d38ecc85d07ac274ee3ca2f155d7e48b9f 100644 (file)
@@ -286,6 +286,10 @@ ifdef CONFIG_FILS
 CFLAGS += -DCONFIG_FILS
 NEED_SHA384=y
 NEED_AES_SIV=y
+ifdef CONFIG_FILS_SK_PFS
+CFLAGS += -DCONFIG_FILS_SK_PFS
+NEED_ECC=y
+endif
 endif
 
 ifdef CONFIG_WNM
index f56b49bb8ad4f2edaa0bc7c2751fab3ae4bc3047..8bfb6a18215c39594668411fbd2c1e58fd006092 100644 (file)
@@ -2147,6 +2147,7 @@ static const struct parse_data ssid_fields[] = {
        { INT_RANGE(mac_addr, 0, 2) },
        { INT_RANGE(pbss, 0, 2) },
        { INT_RANGE(wps_disabled, 0, 1) },
+       { INT_RANGE(fils_dh_group, 0, 65535) },
 };
 
 #undef OFFSET
index 72e0a55eb02ab32002beefa56b258addd59236d6..e8f11493e55978c934ddb36fbd90e384adfd5e23 100644 (file)
@@ -800,6 +800,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        INT(vht_center_freq2);
        INT(pbss);
        INT(wps_disabled);
+       INT(fils_dh_group);
 #ifdef CONFIG_IEEE80211W
        write_int(f, "ieee80211w", ssid->ieee80211w,
                  MGMT_FRAME_PROTECTION_DEFAULT);
index 908b6415df7c33783a0126d296d650fc9328b91d..869165c89602c24ee85dbfefee2f64e13f10c90b 100644 (file)
@@ -814,6 +814,14 @@ struct wpa_ssid {
         * 1 = WPS disabled
         */
        int wps_disabled;
+
+       /**
+        * fils_dh_group - FILS DH Group
+        *
+        * 0 = PFS disabled with FILS shared key authentication
+        * 1-65535 DH Group to use for FILS PFS
+        */
+       int fils_dh_group;
 };
 
 #endif /* CONFIG_SSID_H */
index 7cb86dd3525713a27a49b7cc316b12360de83c77..307f82d8546aeb7c591107b1f1541edb13258eea 100644 (file)
@@ -558,6 +558,8 @@ CONFIG_PEERKEY=y
 # Note: This is an experimental and not yet complete implementation. This
 # should not be enabled for production use.
 #CONFIG_FILS=y
+# FILS shared key authentication with PFS
+#CONFIG_FILS_SK_PFS=y
 
 # Support RSN on IBSS networks
 # This is needed to be able to use mode=1 network profile with proto=RSN and
index 82a8b11aaa8b219fa5f77c8c23564ca563101238..4ef8e28fa095c9840efd32d7cfec26afb2e3c4db 100644 (file)
@@ -2214,7 +2214,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_FILS
 #ifdef CONFIG_SME
-       if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS &&
+       if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
+            wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) &&
            (!data->assoc_info.resp_frame ||
             fils_process_assoc_resp(wpa_s->wpa,
                                     data->assoc_info.resp_frame,
index 76d1acd00b964536d64dc7269cdbf91c2b0f8b5f..5ab56c8272e5dbad5629d3b6c87c9d52709621ff 100644 (file)
@@ -563,12 +563,23 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                    0)
                        wpa_printf(MSG_DEBUG,
                                   "SME: Try to use FILS with PMKSA caching");
-               resp = fils_build_auth(wpa_s->wpa);
+               resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group);
                if (resp) {
-                       params.auth_alg = WPA_AUTH_ALG_FILS;
+                       int auth_alg;
+
+                       if (ssid->fils_dh_group)
+                               wpa_printf(MSG_DEBUG,
+                                          "SME: Try to use FILS SK authentication with PFS (DH Group %u)",
+                                          ssid->fils_dh_group);
+                       else
+                               wpa_printf(MSG_DEBUG,
+                                          "SME: Try to use FILS SK authentication without PFS");
+                       auth_alg = ssid->fils_dh_group ?
+                               WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS;
+                       params.auth_alg = auth_alg;
                        params.auth_data = wpabuf_head(resp);
                        params.auth_data_len = wpabuf_len(resp);
-                       wpa_s->sme.auth_alg = WPA_AUTH_ALG_FILS;
+                       wpa_s->sme.auth_alg = auth_alg;
                }
        }
 #endif /* CONFIG_FILS */
@@ -968,7 +979,27 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_FILS
-       if (data->auth.auth_type == WLAN_AUTH_FILS_SK) {
+       if (data->auth.auth_type == WLAN_AUTH_FILS_SK ||
+           data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) {
+               u16 expect_auth_type;
+
+               expect_auth_type = wpa_s->sme.auth_alg ==
+                       WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS :
+                       WLAN_AUTH_FILS_SK;
+               if (data->auth.auth_type != expect_auth_type) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "SME: FILS Authentication response used different auth alg (%u; expected %u)",
+                               data->auth.auth_type, expect_auth_type);
+                       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+                               MACSTR
+                               " reason=%d locally_generated=1",
+                               MAC2STR(wpa_s->pending_bssid),
+                               WLAN_REASON_DEAUTH_LEAVING);
+                       wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+                       wpa_supplicant_mark_disassoc(wpa_s);
+                       return;
+               }
+
                if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid,
                                      data->auth.ies, data->auth.ies_len) < 0) {
                        wpa_dbg(wpa_s, MSG_DEBUG,
@@ -1010,7 +1041,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        os_memset(&params, 0, sizeof(params));
 
 #ifdef CONFIG_FILS
-       if (auth_type == WLAN_AUTH_FILS_SK) {
+       if (auth_type == WLAN_AUTH_FILS_SK ||
+           auth_type == WLAN_AUTH_FILS_SK_PFS) {
                struct wpabuf *buf;
                const u8 *snonce, *anonce;
                const unsigned int max_hlp = 20;
index 8608681eeb61189e6fc53507ce786a97a4b56276..6dac20e2029270beb8ea68ea21d504b5c66f7c5e 100644 (file)
@@ -1248,6 +1248,11 @@ fast_reauth=1
 # 1 = WPS disabled
 #wps_disabled=0
 
+# FILS DH Group
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 = DH Group to use for FILS PFS
+#fils_dh_group=0
+
 # MAC address policy
 # 0 = use permanent MAC address
 # 1 = use random MAC address for each ESS connection