]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
mesh: Clean up AMPE element encoding and parsing
authorJouni Malinen <j@w1.fi>
Sat, 18 Jun 2016 11:11:23 +0000 (14:11 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 19 Jun 2016 17:18:09 +0000 (20:18 +0300)
The AMPE element includes number of optional and variable length fields
and those cannot really be represented by a fixed struct
ieee80211_ampe_ie. Remove the optional fields from the struct and
build/parse these fields separately.

This is also adding support for IGTKdata that was completely missing
from the previous implementation. In addition, Key RSC for MGTK is now
filled in and used when configuring the RX MGTK for a peer.

Signed-off-by: Jouni Malinen <j@w1.fi>
src/ap/sta_info.h
src/common/ieee802_11_defs.h
wpa_supplicant/driver_i.h
wpa_supplicant/mesh_mpm.c
wpa_supplicant/mesh_rsn.c
wpa_supplicant/mesh_rsn.h

index 2e1e019ef234ff5b5393fd73b93a219bcc14b94d..c374ea0e88946a49a98ede9a9d7cfb15d2f937fd 100644 (file)
@@ -87,10 +87,13 @@ struct sta_info {
        u8 aek[32];     /* SHA256 digest length */
        u8 mtk[WPA_TK_MAX_LEN];
        size_t mtk_len;
+       u8 mgtk_rsc[6];
        u8 mgtk[WPA_TK_MAX_LEN];
        size_t mgtk_len;
+       u8 igtk_rsc[6];
        u8 igtk[WPA_TK_MAX_LEN];
        size_t igtk_len;
+       u16 igtk_key_id;
        u8 sae_auth_retry;
 #endif /* CONFIG_MESH */
 
index 5be747b461ed5ae49fcc0bc894b34b4e9e7ea2d8..8a64859e79289308af1c3b0f088da668abc72bb1 100644 (file)
@@ -757,9 +757,14 @@ struct ieee80211_ampe_ie {
        u8 selected_pairwise_suite[4];
        u8 local_nonce[32];
        u8 peer_nonce[32];
-       u8 mgtk[16];
-       u8 key_rsc[8];
-       u8 key_expiration[4];
+       /* Followed by
+        * Key Replay Counter[8] (optional)
+        *      (only in Mesh Group Key Inform/Acknowledge frames)
+        * GTKdata[variable] (optional)
+        *      (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4])
+        * IGTKdata[variable] (optional)
+        *      (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format)
+        */
 } STRUCT_PACKED;
 
 #ifdef _MSC_VER
index 1d0f96f70aa7ce6391bb3d4df0f6db4b08c9ffd4..9f104f5a20daaca241a7baf0e49595709bd56b20 100644 (file)
@@ -158,6 +158,15 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
+static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s,
+                                    const u8 *addr, int idx, u8 *seq)
+{
+       if (wpa_s->driver->get_seqnum)
+               return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv,
+                                                addr, idx, seq);
+       return -1;
+}
+
 static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
                                     const u8 *addr, int reason_code)
 {
index 9829bccd0adaa2404994d355bb5e1ace102aeeea..74ad762bb175dd5b3d8d00a0ce60d077619f0425 100644 (file)
@@ -798,20 +798,25 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
                wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
                                seq, sizeof(seq), sta->mtk, sta->mtk_len);
 
+               wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC",
+                               sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
                wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
                                sta->mgtk, sta->mgtk_len);
                /* TODO: support for other ciphers */
                /* FIX: key index.. */
                wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
-                               seq, sizeof(seq),
+                               sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
                                sta->mgtk, sta->mgtk_len);
 
                if (sta->igtk_len) {
+                       wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC",
+                                       sta->igtk_rsc, sizeof(sta->igtk_rsc));
                        wpa_hexdump_key(MSG_DEBUG, "RX IGTK",
                                        sta->igtk, sta->igtk_len);
                        /* FIX: key index.. */
-                       wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
-                                       seq, sizeof(seq),
+                       wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr,
+                                       sta->igtk_key_id, 0,
+                                       sta->igtk_rsc, sizeof(sta->igtk_rsc),
                                        sta->igtk, sta->igtk_len);
                }
        }
index db2a6080035d5da4d3f88ebc297e4e3272a8f574..6ca3837ff687d9256cb5abaecb9bf931624e5d44 100644 (file)
@@ -176,17 +176,20 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
        rsn->mgtk_len = wpa_cipher_key_len(WPA_CIPHER_CCMP);
        if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0)
                return -1;
+       rsn->mgtk_key_id = 1;
 
 #ifdef CONFIG_IEEE80211W
        if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-               if (random_get_bytes(rsn->igtk, 16) < 0)
+               rsn->igtk_len = wpa_cipher_key_len(WPA_CIPHER_AES_128_CMAC);
+               if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
                        return -1;
-               rsn->igtk_len = 16;
+               rsn->igtk_key_id = 4;
 
                /* group mgmt */
                wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
                                rsn->igtk, rsn->igtk_len);
-               wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
+               wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL,
+                               rsn->igtk_key_id, 1,
                                seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
        }
 #endif /* CONFIG_IEEE80211W */
@@ -194,7 +197,7 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
        /* group privacy / data frames */
        wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
                        rsn->mgtk, rsn->mgtk_len);
-       wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
+       wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, rsn->mgtk_key_id, 1,
                        seq, sizeof(seq), rsn->mgtk, rsn->mgtk_len);
 
        return 0;
@@ -491,65 +494,89 @@ int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
 {
        struct ieee80211_ampe_ie *ampe;
        u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
-       u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+       u8 *ampe_ie, *pos, *mic_payload;
        const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
        const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
        int ret = 0;
+       size_t len;
 
-       if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+       len = sizeof(*ampe) + rsn->mgtk_len + WPA_KEY_RSC_LEN + 4;
+#ifdef CONFIG_IEEE80211W
+       if (rsn->igtk_len)
+               len += 2 + 6 + rsn->igtk_len;
+#endif /* CONFIG_IEEE80211W */
+
+       if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) {
                wpa_printf(MSG_ERROR, "protect frame: buffer too small");
                return -EINVAL;
        }
 
-       ampe_ie = os_zalloc(2 + sizeof(*ampe));
+       ampe_ie = os_zalloc(2 + len);
        if (!ampe_ie) {
                wpa_printf(MSG_ERROR, "protect frame: out of memory");
                return -ENOMEM;
        }
 
-       mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
-       if (!mic_ie) {
-               wpa_printf(MSG_ERROR, "protect frame: out of memory");
-               ret = -ENOMEM;
-               goto free;
-       }
-
        /*  IE: AMPE */
        ampe_ie[0] = WLAN_EID_AMPE;
-       ampe_ie[1] = sizeof(*ampe);
+       ampe_ie[1] = len;
        ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
 
        RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
-                    wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
+                        RSN_CIPHER_SUITE_CCMP);
        os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN);
        os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN);
-       /* incomplete: see 13.5.4 */
+
+       pos = (u8 *) (ampe + 1);
+
+       /* TODO: Key Replay Counter[8] optionally for
+        * Mesh Group Key Inform/Acknowledge frames */
+
        /* TODO: static mgtk for now since we don't support rekeying! */
-       os_memcpy(ampe->mgtk, rsn->mgtk, 16);
-       /*  TODO: Populate Key RSC */
-       /*  expire in 13 decades or so */
-       os_memset(ampe->key_expiration, 0xff, 4);
+       /*
+        * GTKdata[variable]:
+        * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+        */
+       os_memcpy(pos, rsn->mgtk, rsn->mgtk_len);
+       pos += rsn->mgtk_len;
+       wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos);
+       pos += WPA_KEY_RSC_LEN;
+       /* Use fixed GTKExpirationTime for now */
+       WPA_PUT_LE32(pos, 0xffffffff);
+       pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+       /*
+        * IGTKdata[variable]:
+        * Key ID[2], IPN[6], IGTK[variable]
+        */
+       if (rsn->igtk_len) {
+               WPA_PUT_LE16(pos, rsn->igtk_key_id);
+               pos += 2;
+               wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos);
+               pos += 6;
+               os_memcpy(pos, rsn->igtk, rsn->igtk_len);
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element",
+                       ampe_ie, 2 + len);
 
        /* IE: MIC */
-       mic_ie[0] = WLAN_EID_MIC;
-       mic_ie[1] = AES_BLOCK_SIZE;
-       wpabuf_put_data(buf, mic_ie, 2);
+       wpabuf_put_u8(buf, WLAN_EID_MIC);
+       wpabuf_put_u8(buf, AES_BLOCK_SIZE);
        /* MIC field is output ciphertext */
 
        /* encrypt after MIC */
-       mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
-                                       AES_BLOCK_SIZE);
+       mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
 
-       if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+       if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3,
                            aad, aad_len, mic_payload)) {
                wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
                ret = -ENOMEM;
-               goto free;
        }
 
-free:
        os_free(ampe_ie);
-       os_free(mic_ie);
 
        return ret;
 }
@@ -565,11 +592,12 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
        u8 null_nonce[WPA_NONCE_LEN] = {};
        u8 ampe_eid;
        u8 ampe_ie_len;
-       u8 *ampe_buf, *crypt = NULL;
+       u8 *ampe_buf, *crypt = NULL, *pos, *end;
        size_t crypt_len;
        const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
        const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
                                   (elems->mic - 2) - cat };
+       size_t key_len;
 
        if (!sta->sae) {
                struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
@@ -598,7 +626,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
                return -1;
 
        crypt_len = elems_len - (elems->mic - start);
-       if (crypt_len < 2) {
+       if (crypt_len < 2 + AES_BLOCK_SIZE) {
                wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
                return -1;
        }
@@ -620,10 +648,15 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
                goto free;
        }
 
+       crypt_len -= AES_BLOCK_SIZE;
+       wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element",
+                       ampe_buf, crypt_len);
+
        ampe_eid = *ampe_buf++;
        ampe_ie_len = *ampe_buf++;
 
        if (ampe_eid != WLAN_EID_AMPE ||
+           (size_t) 2 + ampe_ie_len > crypt_len ||
            ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
                wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
                ret = -1;
@@ -631,6 +664,8 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
        }
 
        ampe = (struct ieee80211_ampe_ie *) ampe_buf;
+       pos = (u8 *) (ampe + 1);
+       end = ampe_buf + ampe_ie_len;
        if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 &&
            os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) {
                wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
@@ -639,10 +674,56 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
        }
        os_memcpy(sta->peer_nonce, ampe->local_nonce,
                  sizeof(ampe->local_nonce));
-       os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
-       sta->mgtk_len = sizeof(ampe->mgtk);
 
-       /* todo parse mgtk expiration */
+       /* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge
+        * frames */
+
+       /*
+        * GTKdata[variable]:
+        * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+        */
+       key_len = wpa_cipher_key_len(WPA_CIPHER_CCMP);
+       if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element");
+               ret = -1;
+               goto free;
+       }
+       sta->mgtk_len = key_len;
+       os_memcpy(sta->mgtk, pos, sta->mgtk_len);
+       wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK",
+                       sta->mgtk, sta->mgtk_len);
+       pos += sta->mgtk_len;
+       wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC",
+                   pos, WPA_KEY_RSC_LEN);
+       os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc));
+       pos += WPA_KEY_RSC_LEN;
+       wpa_printf(MSG_DEBUG,
+                  "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds",
+                  WPA_GET_LE32(pos));
+       pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+       /*
+        * IGTKdata[variable]:
+        * Key ID[2], IPN[6], IGTK[variable]
+        */
+       key_len = wpa_cipher_key_len(WPA_CIPHER_AES_128_CMAC);
+       if (end - pos >= (int) (2 + 6 + key_len)) {
+               sta->igtk_key_id = WPA_GET_LE16(pos);
+               wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u",
+                          sta->igtk_key_id);
+               pos += 2;
+               os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc));
+               wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN",
+                           sta->igtk_rsc, sizeof(sta->igtk_rsc));
+               pos += 6;
+               os_memcpy(sta->igtk, pos, key_len);
+               sta->igtk_len = key_len;
+               wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK",
+                               sta->igtk, sta->igtk_len);
+       }
+#endif /* CONFIG_IEEE80211W */
+
 free:
        os_free(crypt);
        return ret;
index 8f2a8e7be991c50254ac012cc28dd0f971735e7e..85fba7dd3f9c939db909ef80a4c0ea1d97e6f777 100644 (file)
@@ -14,6 +14,8 @@ struct mesh_rsn {
        struct wpa_authenticator *auth;
        u8 mgtk[WPA_TK_MAX_LEN];
        size_t mgtk_len;
+       u8 mgtk_key_id;
+       u8 igtk_key_id;
        u8 igtk[WPA_TK_MAX_LEN];
        size_t igtk_len;
 #ifdef CONFIG_SAE