]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: Add FILS SK auth PFS support in AP mode
authorJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 20:40:56 +0000 (22:40 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 12 Mar 2017 21:20:32 +0000 (23:20 +0200)
This adds an option to configure hostapd to enable use of 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 configuration parameter fils_dh_group is used to enable this
by specifying which DH group to use. For example, fils_dh_group=19 would
allow FILS SK PFS to be used with a 256-bit random ECP group.

Signed-off-by: Jouni Malinen <j@w1.fi>
hostapd/Android.mk
hostapd/Makefile
hostapd/config_file.c
hostapd/defconfig
hostapd/hostapd.conf
src/ap/ap_config.h
src/ap/ieee802_11.c
src/ap/ieee802_11_shared.c
src/ap/sta_info.c
src/ap/sta_info.h

index 10018bceaec55d84f6649728d080510e2c59745a..b1940f80298cf87907ce4bc0a1add676033420f6 100644 (file)
@@ -272,6 +272,10 @@ L_CFLAGS += -DCONFIG_FILS
 OBJS += src/ap/fils_hlp.c
 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 34a0e68a19a16bdecc9b82998374d27fc1fc9001..c443618efbb55f880d42daf8392c0415b12c2e8c 100644 (file)
@@ -316,6 +316,10 @@ CFLAGS += -DCONFIG_FILS
 OBJS += ../src/ap/fils_hlp.o
 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 2e4433d0792574ef3cf379acca77ed7b54cd809e..7b438060541f81c85ac5064632659cc023a70c2d 100644 (file)
@@ -3658,6 +3658,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "fils_realm") == 0) {
                if (parse_fils_realm(bss, pos) < 0)
                        return 1;
+       } else if (os_strcmp(buf, "fils_dh_group") == 0) {
+               bss->fils_dh_group = atoi(pos);
        } else if (os_strcmp(buf, "dhcp_server") == 0) {
                if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
                        wpa_printf(MSG_ERROR,
index 2370fc12c8984df17e82e7268f68b658eb7a29ac..521d877eb4006a11dbfeaaf3d5190b60723d29e0 100644 (file)
@@ -358,6 +358,8 @@ CONFIG_IPV6=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
 
 # Include internal line edit mode in hostapd_cli. This can be used to provide
 # limited command line editing and history support.
index 19596ce001e917509b44cd2c62bb99ad53513eae..18c330babca636205b369e5cb1d36849e609da7d 100644 (file)
@@ -1393,6 +1393,11 @@ own_ip_addr=127.0.0.1
 #fils_realm=example.com
 #fils_realm=example.org
 
+# FILS DH Group for PFS
+# 0 = PFS disabled with FILS shared key authentication (default)
+# 1-65535 DH Group to use for FILS PFS
+#fils_dh_group=0
+
 # DHCP server for FILS HLP
 # If configured, hostapd will act as a DHCP relay for all FILS HLP requests
 # that include a DHCPDISCOVER message and send them to the specific DHCP
index 5394339074fdf269cc4c30b85cf6acf0a5b0d602..989b07107c9a2abe40a3828fb3ae043d59978743 100644 (file)
@@ -610,6 +610,7 @@ struct hostapd_bss_config {
        u8 fils_cache_id[FILS_CACHE_ID_LEN];
        int fils_cache_id_set;
        struct dl_list fils_realms; /* list of struct fils_realm */
+       int fils_dh_group;
        struct hostapd_ip_addr dhcp_server;
        int dhcp_rapid_commit_proxy;
        unsigned int fils_hlp_wait_time;
index 5bbd585fb78c061443ddd8a5e2a5c613471300f6..ec733295728ab5b0b3409f874510cfe4c35d8b33 100644 (file)
@@ -1014,8 +1014,9 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
                                    const u8 *msk, size_t msk_len);
 
 static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
-                           const struct ieee80211_mgmt *mgmt, size_t len,
-                           u16 auth_transaction, u16 status_code)
+                            const struct ieee80211_mgmt *mgmt, size_t len,
+                            u16 auth_alg, u16 auth_transaction,
+                            u16 status_code)
 {
        u16 resp = WLAN_STATUS_SUCCESS;
        const u8 *pos, *end;
@@ -1033,8 +1034,76 @@ static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
        wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
                    pos, end - pos);
 
-       /* 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 (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
+               u16 group;
+               struct wpabuf *pub;
+               size_t elem_len;
+
+               /* Using FILS PFS */
+
+               /* Finite Cyclic Group */
+               if (end - pos < 2) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: No room for Finite Cyclic Group");
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+               group = WPA_GET_LE16(pos);
+               pos += 2;
+               if (group != hapd->conf->fils_dh_group) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
+                                  group, hapd->conf->fils_dh_group);
+                       resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+                       goto fail;
+               }
+
+               crypto_ecdh_deinit(sta->fils_ecdh);
+               sta->fils_ecdh = crypto_ecdh_init(group);
+               if (!sta->fils_ecdh) {
+                       wpa_printf(MSG_INFO,
+                                  "FILS: Could not initialize ECDH with group %d",
+                                  group);
+                       resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+                       goto fail;
+               }
+
+               pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+               if (!pub) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: Failed to derive ECDH public key");
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+               elem_len = wpabuf_len(pub);
+               wpabuf_free(pub);
+
+               /* Element */
+               if ((size_t) (end - pos) < elem_len) {
+                       wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+
+               wpabuf_clear_free(sta->fils_dh_ss);
+               sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
+                                                         pos, elem_len);
+               if (!sta->fils_dh_ss) {
+                       wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+               wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
+               pos += elem_len;
+       } else {
+               crypto_ecdh_deinit(sta->fils_ecdh);
+               sta->fils_ecdh = NULL;
+               wpabuf_clear_free(sta->fils_dh_ss);
+               sta->fils_dh_ss = NULL;
+       }
+#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) {
@@ -1173,6 +1242,8 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
        const u8 *pmk = NULL;
        size_t pmk_len = 0;
        u8 pmk_buf[PMK_LEN_MAX];
+       struct wpabuf *pub = NULL;
+       u16 auth_alg;
 
        if (resp != WLAN_STATUS_SUCCESS)
                goto fail;
@@ -1204,14 +1275,32 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
        wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
                    fils_nonce, FILS_NONCE_LEN);
 
-       data = wpabuf_alloc(1000 + ielen);
+#ifdef CONFIG_FILS_SK_PFS
+       if (sta->fils_dh_ss && sta->fils_ecdh) {
+               pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
+               if (!pub) {
+                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto fail;
+               }
+       }
+#endif /* CONFIG_FILS_SK_PFS */
+
+       data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
        if (!data) {
                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
                goto fail;
        }
 
-       /* 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 (pub) {
+               /* Finite Cyclic Group */
+               wpabuf_put_le16(data, hapd->conf->fils_dh_group);
+
+               /* Element */
+               wpabuf_put_buf(data, pub);
+       }
+#endif /* CONFIG_FILS_SK_PFS */
 
        /* RSNE */
        wpabuf_put_data(data, ie, ielen);
@@ -1243,7 +1332,11 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
 
                if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
                                     msk, msk_len, sta->fils_snonce, fils_nonce,
-                                    NULL, 0, pmk_buf, &pmk_len)) {
+                                    sta->fils_dh_ss ?
+                                    wpabuf_head(sta->fils_dh_ss) : NULL,
+                                    sta->fils_dh_ss ?
+                                    wpabuf_len(sta->fils_dh_ss) : 0,
+                                    pmk_buf, &pmk_len)) {
                        wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
                        resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
                        wpabuf_free(data);
@@ -1273,8 +1366,10 @@ static void handle_auth_fils_finish(struct hostapd_data *hapd,
        }
 
 fail:
-       send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2,
-                       resp,
+       auth_alg = (pub ||
+                   resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
+               WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
+       send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp,
                        data ? wpabuf_head(data) : (u8 *) "",
                        data ? wpabuf_len(data) : 0);
        wpabuf_free(data);
@@ -1285,11 +1380,18 @@ fail:
                               "authentication OK (FILS)");
                sta->flags |= WLAN_STA_AUTH;
                wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-               sta->auth_alg = WLAN_AUTH_FILS_SK;
+               sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
                mlme_authenticate_indication(hapd, sta);
        }
 
        os_free(ie_buf);
+       wpabuf_free(pub);
+       wpabuf_clear_free(sta->fils_dh_ss);
+       sta->fils_dh_ss = NULL;
+#ifdef CONFIG_FILS_SK_PFS
+       crypto_ecdh_deinit(sta->fils_ecdh);
+       sta->fils_ecdh = NULL;
+#endif /* CONFIG_FILS_SK_PFS */
 }
 
 
@@ -1472,6 +1574,9 @@ static void handle_auth(struct hostapd_data *hapd,
 #ifdef CONFIG_FILS
              (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
               auth_alg == WLAN_AUTH_FILS_SK) ||
+             (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+              hapd->conf->fils_dh_group &&
+              auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
 #endif /* CONFIG_FILS */
              ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
               auth_alg == WLAN_AUTH_SHARED_KEY))) {
@@ -1738,8 +1843,9 @@ static void handle_auth(struct hostapd_data *hapd,
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
        case WLAN_AUTH_FILS_SK:
-               handle_auth_fils(hapd, sta, mgmt, len, auth_transaction,
-                                status_code);
+       case WLAN_AUTH_FILS_SK_PFS:
+               handle_auth_fils(hapd, sta, mgmt, len, auth_alg,
+                                auth_transaction, status_code);
                return;
 #endif /* CONFIG_FILS */
        }
index daacf7e44ac8578296ca719d6678b72798e91b20..4d5ec2f6b40a4411bb7128e9c676f8bcdc2c903e 100644 (file)
@@ -630,7 +630,10 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
                fils_info |= BIT(8); /* HESSID Included */
        /* FILS Shared Key Authentication without PFS Supported */
        fils_info |= BIT(9);
-       /* TODO: B10: FILS Shared Key Authentication with PFS Supported */
+       if (hapd->conf->fils_dh_group) {
+               /* FILS Shared Key Authentication with PFS Supported */
+               fils_info |= BIT(10);
+       }
        /* TODO: B11: FILS Public Key Authentication Supported */
        /* B12..B15: Reserved */
        WPA_PUT_LE16(pos, fils_info);
index f233d753addf673eec25faf7b341d345197b9543..ea217f3872932d9c3fc450b5ef23fe14b70ace2b 100644 (file)
@@ -345,6 +345,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        wpabuf_free(sta->fils_hlp_resp);
        wpabuf_free(sta->hlp_dhcp_discover);
        eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#ifdef CONFIG_FILS_SK_PFS
+       crypto_ecdh_deinit(sta->fils_ecdh);
+       wpabuf_clear_free(sta->fils_dh_ss);
+#endif /* CONFIG_FILS_SK_PFS */
 #endif /* CONFIG_FILS */
 
 #ifdef CONFIG_OWE
index b5d1d33155848631b0f9f21ec37f3388a15b6073..029579841a73afedc3e4f7f141fa0a5d9aa3005b 100644 (file)
@@ -231,6 +231,10 @@ struct sta_info {
        unsigned int fils_dhcp_rapid_commit_proxy:1;
        struct wpabuf *fils_hlp_resp;
        struct wpabuf *hlp_dhcp_discover;
+#ifdef CONFIG_FILS_SK_PFS
+       struct crypto_ecdh *fils_ecdh;
+#endif /* CONFIG_FILS_SK_PFS */
+       struct wpabuf *fils_dh_ss;
 #endif /* CONFIG_FILS */
 
 #ifdef CONFIG_OWE