]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: Derive FT key hierarchy on supplicant side for FILS+FT
authorJouni Malinen <j@w1.fi>
Sun, 7 May 2017 09:00:27 +0000 (12:00 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 7 May 2017 19:08:41 +0000 (22:08 +0300)
Derive PMK-R0 and the relevant key names when using FILS authentication
for initial FT mobility domain association. Fill in the FT IEs in
(Re)Association Request frame for this.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/rsn_supp/wpa.c
src/rsn_supp/wpa_i.h
wpa_supplicant/sme.c

index 79504de7ab58df6febaa5035f85fd34024ba44e3..4e5b0a4f749f48d2bcf0eddee36ccdb8938333ef 100644 (file)
@@ -2466,6 +2466,9 @@ void wpa_sm_deinit(struct wpa_sm *sm)
 #ifdef CONFIG_FILS_SK_PFS
        crypto_ecdh_deinit(sm->fils_ecdh);
 #endif /* CONFIG_FILS_SK_PFS */
+#ifdef CONFIG_FILS
+       wpabuf_free(sm->fils_ft_ies);
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_OWE
        crypto_ecdh_deinit(sm->owe_ecdh);
 #endif /* CONFIG_OWE */
@@ -3503,8 +3506,53 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
        wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
 
-       /* TODO: MDE when using FILS+FT */
-       /* TODO: FTE when using FILS+FT */
+       if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+               struct wpa_ft_ies parse;
+
+               if (!elems.mdie || !elems.ftie) {
+                       wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
+                       goto fail;
+               }
+
+               if (wpa_ft_parse_ies(pos, end - pos, &parse) < 0) {
+                       wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
+                       goto fail;
+               }
+
+               if (!parse.r0kh_id) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS+FT: No R0KH-ID subelem in FTE");
+                       goto fail;
+               }
+               os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+               sm->r0kh_id_len = parse.r0kh_id_len;
+               wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+                                 sm->r0kh_id, sm->r0kh_id_len);
+
+               if (!parse.r1kh_id) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS+FT: No R1KH-ID subelem in FTE");
+                       goto fail;
+               }
+               os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+               wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID",
+                           sm->r1kh_id, FT_R1KH_ID_LEN);
+
+               /* TODO: Check MDE and FTE payload */
+
+               wpabuf_free(sm->fils_ft_ies);
+               sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len +
+                                              2 + elems.ftie_len);
+               if (!sm->fils_ft_ies)
+                       goto fail;
+               wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2,
+                               2 + elems.mdie_len);
+               wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2,
+                               2 + elems.ftie_len);
+       } else {
+               wpabuf_free(sm->fils_ft_ies);
+               sm->fils_ft_ies = NULL;
+       }
 
        /* PMKID List */
        if (rsn.pmkid && rsn.num_pmkid > 0) {
@@ -3604,7 +3652,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
                            sm->fils_nonce, sm->fils_anonce, &sm->ptk,
                            ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher,
-                           NULL, NULL) < 0) {
+                           sm->fils_ft, &sm->fils_ft_len) < 0) {
                wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
                goto fail;
        }
@@ -3648,6 +3696,110 @@ fail:
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
+{
+       struct rsn_ie_hdr *rsnie;
+       u16 capab;
+       u8 *pos;
+
+       /* RSNIE[PMKR0Name/PMKR1Name] */
+       rsnie = wpabuf_put(buf, sizeof(*rsnie));
+       rsnie->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+
+       /* Group Suite Selector */
+       if (!wpa_cipher_valid_group(sm->group_cipher)) {
+               wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+                          sm->group_cipher);
+               return -1;
+       }
+       pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+       RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+                                                 sm->group_cipher));
+
+       /* Pairwise Suite Count */
+       wpabuf_put_le16(buf, 1);
+
+       /* Pairwise Suite List */
+       if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+               wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+                          sm->pairwise_cipher);
+               return -1;
+       }
+       pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+       RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+                                                 sm->pairwise_cipher));
+
+       /* Authenticated Key Management Suite Count */
+       wpabuf_put_le16(buf, 1);
+
+       /* Authenticated Key Management Suite List */
+       pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+       if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+       else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+       else {
+               wpa_printf(MSG_WARNING,
+                          "FILS+FT: Invalid key management type (%d)",
+                          sm->key_mgmt);
+               return -1;
+       }
+
+       /* RSN Capabilities */
+       capab = 0;
+#ifdef CONFIG_IEEE80211W
+       if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+               capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+       wpabuf_put_le16(buf, capab);
+
+       /* PMKID Count */
+       wpabuf_put_le16(buf, 1);
+
+       /* PMKID List [PMKR1Name] */
+       wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)",
+                       sm->fils_ft, sm->fils_ft_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len);
+       wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID",
+                   sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+       wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
+                         sm->r0kh_id, sm->r0kh_id_len);
+       if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
+                             sm->ssid_len, sm->mobility_domain,
+                             sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+                             sm->pmk_r0, sm->pmk_r0_name) < 0) {
+               wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+       wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
+                   sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+       wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
+                  MAC2STR(sm->r1kh_id));
+       pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
+       if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
+                                  pos) < 0) {
+               wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN);
+
+#ifdef CONFIG_IEEE80211W
+       if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+               /* Management Group Cipher Suite */
+               pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
+       return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
                                     size_t *kek_len, const u8 **snonce,
                                     const u8 **anonce,
@@ -3659,12 +3811,30 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
        unsigned int i;
 
        len = 1000;
+#ifdef CONFIG_IEEE80211R
+       if (sm->fils_ft_ies)
+               len += wpabuf_len(sm->fils_ft_ies);
+       if (wpa_key_mgmt_ft(sm->key_mgmt))
+               len += 256;
+#endif /* CONFIG_IEEE80211R */
        for (i = 0; hlp && i < num_hlp; i++)
                len += 10 + wpabuf_len(hlp[i]);
        buf = wpabuf_alloc(len);
        if (!buf)
                return NULL;
 
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+               /* MDE and FTE when using FILS+FT */
+               wpabuf_put_buf(buf, sm->fils_ft_ies);
+               /* RSNE with PMKR1Name in PMKID field */
+               if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_IEEE80211R */
+
        /* FILS Session */
        wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
        wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
index 62cd3c1149c8c181a061ecbfc58157b70150ab3f..82e194188081a71af4aff25b0f6eb1f81883136d 100644 (file)
@@ -154,6 +154,9 @@ struct wpa_sm {
        struct crypto_ecdh *fils_ecdh;
        int fils_dh_group;
        size_t fils_dh_elem_len;
+       struct wpabuf *fils_ft_ies;
+       u8 fils_ft[FILS_FT_MAX_LEN];
+       size_t fils_ft_len;
 #endif /* CONFIG_FILS */
 
 #ifdef CONFIG_OWE
index 7c097e4e758691ed73575c0f5503d1e7ce421800..08315a5cf4e59b7f5dae8a1e8a7182aa16e4786e 100644 (file)
@@ -1058,6 +1058,22 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 }
 
 
+#ifdef CONFIG_IEEE80211R
+static void remove_ie(u8 *buf, size_t *len, u8 eid)
+{
+       u8 *pos, *next, *end;
+
+       pos = (u8 *) get_ie(buf, *len, eid);
+       if (pos) {
+               next = pos + 2 + pos[1];
+               end = buf + *len;
+               *len -= 2 + pos[1];
+               os_memmove(pos, next, end - next);
+       }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
 void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                   const u8 *bssid, u16 auth_type)
 {
@@ -1113,6 +1129,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                        wpabuf_free(hlp[i]);
                if (!buf)
                        return;
+               wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
+                           wpa_s->sme.assoc_req_ie,
+                           wpa_s->sme.assoc_req_ie_len);
+#ifdef CONFIG_IEEE80211R
+               if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+                       /* Remove RSNE and MDE to allow them to be overridden
+                        * with FILS+FT specific values from
+                        * fils_build_assoc_req(). */
+                       remove_ie(wpa_s->sme.assoc_req_ie,
+                                 &wpa_s->sme.assoc_req_ie_len,
+                                 WLAN_EID_RSN);
+                       wpa_hexdump(MSG_DEBUG,
+                                   "FILS: assoc_req after RSNE removal",
+                                   wpa_s->sme.assoc_req_ie,
+                                   wpa_s->sme.assoc_req_ie_len);
+                       remove_ie(wpa_s->sme.assoc_req_ie,
+                                 &wpa_s->sme.assoc_req_ie_len,
+                                 WLAN_EID_MOBILITY_DOMAIN);
+                       wpa_hexdump(MSG_DEBUG,
+                                   "FILS: assoc_req after MDE removal",
+                                   wpa_s->sme.assoc_req_ie,
+                                   wpa_s->sme.assoc_req_ie_len);
+               }
+#endif /* CONFIG_IEEE80211R */
                /* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
                if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
                    sizeof(wpa_s->sme.assoc_req_ie)) {
@@ -1125,6 +1165,9 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                          wpabuf_head(buf), wpabuf_len(buf));
                wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
                wpabuf_free(buf);
+               wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
+                           wpa_s->sme.assoc_req_ie,
+                           wpa_s->sme.assoc_req_ie_len);
 
                os_memcpy(nonces, snonce, FILS_NONCE_LEN);
                os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);