TDLS: Declare tdls_testing as extern in a header file
[mech_eap.git] / wpa_supplicant / mesh_rsn.c
index c82c539..c5f5d69 100644 (file)
@@ -146,17 +146,17 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
        wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
 
        os_memset(&conf, 0, sizeof(conf));
-       conf.wpa = 2;
+       conf.wpa = WPA_PROTO_RSN;
        conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
-       conf.wpa_pairwise = WPA_CIPHER_CCMP;
-       conf.rsn_pairwise = WPA_CIPHER_CCMP;
-       conf.wpa_group = WPA_CIPHER_CCMP;
+       conf.wpa_pairwise = rsn->pairwise_cipher;
+       conf.rsn_pairwise = rsn->pairwise_cipher;
+       conf.wpa_group = rsn->group_cipher;
        conf.eapol_version = 0;
        conf.wpa_group_rekey = -1;
 #ifdef CONFIG_IEEE80211W
        conf.ieee80211w = ieee80211w;
        if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
-               conf.group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+               conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
 #endif /* CONFIG_IEEE80211W */
 
        os_memset(&cb, 0, sizeof(cb));
@@ -173,16 +173,34 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
        }
 
        /* TODO: support rekeying */
-       if (random_get_bytes(rsn->mgtk, 16) < 0)
+       rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group);
+       if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0)
                return -1;
+       rsn->mgtk_key_id = 1;
 
-       /* group mgmt */
-       wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
-                       seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+#ifdef CONFIG_IEEE80211W
+       if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher);
+               if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
+                       return -1;
+               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_cipher_to_alg(rsn->mgmt_group_cipher), NULL,
+                               rsn->igtk_key_id, 1,
+                               seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
+       }
+#endif /* CONFIG_IEEE80211W */
 
        /* group privacy / data frames */
-       wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
-                       seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+       wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
+                       rsn->mgtk, rsn->mgtk_len);
+       wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL,
+                       rsn->mgtk_key_id, 1, seq, sizeof(seq),
+                       rsn->mgtk, rsn->mgtk_len);
 
        return 0;
 }
@@ -191,6 +209,9 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
 static void mesh_rsn_deinit(struct mesh_rsn *rsn)
 {
        os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
+       rsn->mgtk_len = 0;
+       os_memset(rsn->igtk, 0, sizeof(rsn->igtk));
+       rsn->igtk_len = 0;
        if (rsn->auth)
                wpa_deinit(rsn->auth);
 }
@@ -208,6 +229,9 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
        if (mesh_rsn == NULL)
                return NULL;
        mesh_rsn->wpa_s = wpa_s;
+       mesh_rsn->pairwise_cipher = conf->pairwise_cipher;
+       mesh_rsn->group_cipher = conf->group_cipher;
+       mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher;
 
        if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr,
                                 conf->ieee80211w) < 0) {
@@ -395,13 +419,16 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
 {
        u8 *ptr;
        u8 *min, *max;
-       u16 min_lid, max_lid;
-       size_t lid_len = sizeof(sta->my_lid);
        u8 *myaddr = wpa_s->own_addr;
        u8 *peer = sta->addr;
-       /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
-       u8 context[64 + 4 + 4 + 12];
+       u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN];
 
+       /*
+        * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce,
+        *  peerNonce) || max(localNonce, peerNonce) || min(localLinkID,
+        *  peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite ||
+        *  min(localMAC, peerMAC) || max(localMAC, peerMAC))
+        */
        ptr = context;
        if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) {
                min = sta->my_nonce;
@@ -411,23 +438,25 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
                max = sta->my_nonce;
        }
        os_memcpy(ptr, min, WPA_NONCE_LEN);
-       os_memcpy(ptr + WPA_NONCE_LEN, max, WPA_NONCE_LEN);
-       ptr += 2 * WPA_NONCE_LEN;
+       ptr += WPA_NONCE_LEN;
+       os_memcpy(ptr, max, WPA_NONCE_LEN);
+       ptr += WPA_NONCE_LEN;
 
        if (sta->my_lid < sta->peer_lid) {
-               min_lid = host_to_le16(sta->my_lid);
-               max_lid = host_to_le16(sta->peer_lid);
+               WPA_PUT_LE16(ptr, sta->my_lid);
+               ptr += 2;
+               WPA_PUT_LE16(ptr, sta->peer_lid);
+               ptr += 2;
        } else {
-               min_lid = host_to_le16(sta->peer_lid);
-               max_lid = host_to_le16(sta->my_lid);
+               WPA_PUT_LE16(ptr, sta->peer_lid);
+               ptr += 2;
+               WPA_PUT_LE16(ptr, sta->my_lid);
+               ptr += 2;
        }
-       os_memcpy(ptr, &min_lid, lid_len);
-       os_memcpy(ptr + lid_len, &max_lid, lid_len);
-       ptr += 2 * lid_len;
 
-       /* SAE */
-       RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
-       ptr += 4;
+       /* Selected AKM Suite: SAE */
+       RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
+       ptr += RSN_SELECTOR_LEN;
 
        if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
                min = myaddr;
@@ -437,11 +466,13 @@ int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
                max = myaddr;
        }
        os_memcpy(ptr, min, ETH_ALEN);
-       os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN);
+       ptr += ETH_ALEN;
+       os_memcpy(ptr, max, ETH_ALEN);
 
-       sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk),
+       sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher);
+       sha256_prf(sta->sae->pmk, SAE_PMK_LEN,
                   "Temporal Key Derivation", context, sizeof(context),
-                  sta->mtk, sizeof(sta->mtk));
+                  sta->mtk, sta->mtk_len);
        return 0;
 }
 
@@ -468,65 +499,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;
 }
@@ -542,11 +597,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];
@@ -575,7 +631,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;
        }
@@ -597,10 +653,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;
@@ -608,6 +669,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");
@@ -616,9 +679,57 @@ 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));
 
-       /* 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]
+        */
+       sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */
+       key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher);
+       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_s->mesh_rsn->mgmt_group_cipher);
+       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;