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 2e1e019..c374ea0 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 5be747b..8a64859 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 1d0f96f..9f104f5 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 9829bcc..74ad762 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 db2a608..6ca3837 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 8f2a8e7..85fba7d 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