]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/rsn_supp/wpa.c
RSN: Report completion only after IGTK configuration
[thirdparty/hostap.git] / src / rsn_supp / wpa.c
index 2c3c814ae4fab9f44446c1a6bcdf23cfd5ee3299..eabe88bc07b26440d565d13c123ea04b87e41f4f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
  * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
 #include "crypto/random.h"
 #include "crypto/aes_siv.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/ocv.h"
 #include "eap_common/eap_defs.h"
 #include "eapol_supp/eapol_supp_sm.h"
+#include "drivers/driver.h"
 #include "wpa.h"
 #include "eloop.h"
 #include "preauth.h"
 #include "pmksa_cache.h"
 #include "wpa_i.h"
 #include "wpa_ie.h"
-#include "peerkey.h"
 
 
 static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -49,8 +52,11 @@ int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
                       u8 *msg, size_t msg_len, u8 *key_mic)
 {
        int ret = -1;
-       size_t mic_len = wpa_mic_len(sm->key_mgmt);
+       size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 
+       wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR
+                  " ver=%d mic_len=%d key_mgmt=0x%x",
+                  MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt);
        if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
                /*
                 * Association event was not yet received; try to fetch
@@ -177,8 +183,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
        int key_info, ver;
        u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
 
-       if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
-           wpa_key_mgmt_suite_b(sm->key_mgmt))
+       if (wpa_use_akm_defined(sm->key_mgmt))
                ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
        else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
                 wpa_key_mgmt_sha256(sm->key_mgmt))
@@ -194,7 +199,7 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
                return;
        }
 
-       mic_len = wpa_mic_len(sm->key_mgmt);
+       mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
        hdrlen = sizeof(*reply) + mic_len + 2;
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
                                  hdrlen, &rlen, (void *) &reply);
@@ -265,7 +270,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                 * event before receiving this 1/4 message, so try to find a
                 * matching PMKSA cache entry here. */
                sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
-                                               NULL);
+                                               NULL, 0);
                if (sm->cur_pmksa) {
                        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                                "RSN: found matching PMKID from PMKSA cache");
@@ -285,6 +290,18 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                eapol_sm_notify_cached(sm->eapol);
 #ifdef CONFIG_IEEE80211R
                sm->xxkey_len = 0;
+#ifdef CONFIG_SAE
+               if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE &&
+                   sm->pmk_len == PMK_LEN) {
+                       /* Need to allow FT key derivation to proceed with
+                        * PMK from SAE being used as the XXKey in cases where
+                        * the PMKID in msg 1/4 matches the PMKSA entry that was
+                        * just added based on SAE authentication for the
+                        * initial mobility domain association. */
+                       os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len);
+                       sm->xxkey_len = sm->pmk_len;
+               }
+#endif /* CONFIG_SAE */
 #endif /* CONFIG_IEEE80211R */
        } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
                int res, pmk_len;
@@ -308,8 +325,15 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                        u8 buf[2 * PMK_LEN];
                        if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
                        {
-                               os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
-                               sm->xxkey_len = PMK_LEN;
+                               if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
+                                       os_memcpy(sm->xxkey, buf,
+                                                 SHA384_MAC_LEN);
+                                       sm->xxkey_len = SHA384_MAC_LEN;
+                               } else {
+                                       os_memcpy(sm->xxkey, buf + PMK_LEN,
+                                                 PMK_LEN);
+                                       sm->xxkey_len = PMK_LEN;
+                               }
                                os_memset(buf, 0, sizeof(buf));
                        }
 #endif /* CONFIG_IEEE80211R */
@@ -339,8 +363,8 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                                                     fils_cache_id);
                        }
                        if (!sm->cur_pmksa && pmkid &&
-                           pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
-                       {
+                           pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
+                                   0)) {
                                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                                        "RSN: the new PMK matches with the "
                                        "PMKID");
@@ -466,7 +490,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 
        wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
-       mic_len = wpa_mic_len(sm->key_mgmt);
+       mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
        hdrlen = sizeof(*reply) + mic_len + 2;
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
                                  NULL, hdrlen + wpa_ie_len,
@@ -510,15 +534,25 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
                          const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
+       const u8 *z = NULL;
+       size_t z_len = 0;
+
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->key_mgmt))
                return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_DPP2
+       if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+               z = wpabuf_head(sm->dpp_z);
+               z_len = wpabuf_len(sm->dpp_z);
+       }
+#endif /* CONFIG_DPP2 */
+
        return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
                              sm->own_addr, sm->bssid, sm->snonce,
                              key->key_nonce, ptk, sm->key_mgmt,
-                             sm->pairwise_cipher);
+                             sm->pairwise_cipher, z, z_len);
 }
 
 
@@ -581,7 +615,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
        /* Calculate PTK which will be stored as a temporary PTK until it has
         * been verified when processing message 3/4. */
        ptk = &sm->tptk;
-       wpa_derive_ptk(sm, src_addr, key, ptk);
+       if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+               goto failed;
        if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
                u8 buf[8];
                /* Supplicant: swap tx/rx Mic keys */
@@ -591,11 +626,37 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
                os_memset(buf, 0, sizeof(buf));
        }
        sm->tptk_set = 1;
-       sm->tk_to_set = 1;
 
        kde = sm->assoc_wpa_ie;
        kde_len = sm->assoc_wpa_ie_len;
 
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm)) {
+               struct wpa_channel_info ci;
+               u8 *pos;
+
+               if (wpa_sm_channel_info(sm, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to get channel info for OCI element in EAPOL-Key 2/4");
+                       goto failed;
+               }
+
+               kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3);
+               if (!kde_buf) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4");
+                       goto failed;
+               }
+
+               os_memcpy(kde_buf, kde, kde_len);
+               kde = kde_buf;
+               pos = kde + kde_len;
+               if (ocv_insert_oci_kde(&ci, &pos) < 0)
+                       goto failed;
+               kde_len = pos - kde;
+       }
+#endif /* CONFIG_OCV */
+
 #ifdef CONFIG_P2P
        if (sm->p2p) {
                kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
@@ -654,6 +715,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
                        MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
                eapol_sm_notify_portValid(sm->eapol, TRUE);
                if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+                   sm->key_mgmt == WPA_KEY_MGMT_DPP ||
                    sm->key_mgmt == WPA_KEY_MGMT_OWE)
                        eapol_sm_notify_eap_success(sm->eapol, TRUE);
                /*
@@ -663,7 +725,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
                 * likelihood of the first preauth EAPOL-Start frame getting to
                 * the target AP.
                 */
-               eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+               if (!dl_list_empty(&sm->pmksa_candidates))
+                       eloop_register_timeout(1, 0, wpa_sm_start_preauth,
+                                              sm, NULL);
        }
 
        if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
@@ -697,7 +761,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
        enum wpa_alg alg;
        const u8 *key_rsc;
 
-       if (!sm->tk_to_set) {
+       if (sm->ptk.installed) {
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                        "WPA: Do not re-install same PTK to the driver");
                return 0;
@@ -721,6 +785,11 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 
        alg = wpa_cipher_to_alg(sm->pairwise_cipher);
        keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+       if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+               wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu",
+                          keylen, (long unsigned int) sm->ptk.tk_len);
+               return -1;
+       }
        rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 
        if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
@@ -741,7 +810,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 
        /* TK is not needed anymore in supplicant */
        os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
-       sm->tk_to_set = 0;
+       sm->ptk.tk_len = 0;
+       sm->ptk.installed = 1;
 
        if (sm->wpa_ptk_rekey) {
                eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
@@ -791,11 +861,23 @@ struct wpa_gtk_data {
 
 static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
                                      const struct wpa_gtk_data *gd,
-                                     const u8 *key_rsc)
+                                     const u8 *key_rsc, int wnm_sleep)
 {
        const u8 *_gtk = gd->gtk;
        u8 gtk_buf[32];
 
+       /* Detect possible key reinstallation */
+       if ((sm->gtk.gtk_len == (size_t) gd->gtk_len &&
+            os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) ||
+           (sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len &&
+            os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk,
+                      sm->gtk_wnm_sleep.gtk_len) == 0)) {
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
+                       gd->keyidx, gd->tx, gd->gtk_len);
+               return 0;
+       }
+
        wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
@@ -830,6 +912,15 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
        }
        os_memset(gtk_buf, 0, sizeof(gtk_buf));
 
+       if (wnm_sleep) {
+               sm->gtk_wnm_sleep.gtk_len = gd->gtk_len;
+               os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk,
+                         sm->gtk_wnm_sleep.gtk_len);
+       } else {
+               sm->gtk.gtk_len = gd->gtk_len;
+               os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
+       }
+
        return 0;
 }
 
@@ -922,7 +1013,7 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
            (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
                                               gtk_len, gtk_len,
                                               &gd.key_rsc_len, &gd.alg) ||
-            wpa_supplicant_install_gtk(sm, &gd, key_rsc))) {
+            wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) {
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                        "RSN: Failed to install GTK");
                os_memset(&gd, 0, sizeof(gd));
@@ -930,12 +1021,62 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
        }
        os_memset(&gd, 0, sizeof(gd));
 
-       wpa_supplicant_key_neg_complete(sm, sm->bssid,
-                                       key_info & WPA_KEY_INFO_SECURE);
        return 0;
 }
 
 
+#ifdef CONFIG_IEEE80211W
+static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
+                                      const struct wpa_igtk_kde *igtk,
+                                      int wnm_sleep)
+{
+       size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+       u16 keyidx = WPA_GET_LE16(igtk->keyid);
+
+       /* Detect possible key reinstallation */
+       if ((sm->igtk.igtk_len == len &&
+            os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) ||
+           (sm->igtk_wnm_sleep.igtk_len == len &&
+            os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk,
+                      sm->igtk_wnm_sleep.igtk_len) == 0)) {
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
+                       keyidx);
+               return  0;
+       }
+
+       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+               "WPA: IGTK keyid %d pn " COMPACT_MACSTR,
+               keyidx, MAC2STR(igtk->pn));
+       wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
+       if (keyidx > 4095) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Invalid IGTK KeyID %d", keyidx);
+               return -1;
+       }
+       if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+                          broadcast_ether_addr,
+                          keyidx, 0, igtk->pn, sizeof(igtk->pn),
+                          igtk->igtk, len) < 0) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Failed to configure IGTK to the driver");
+               return -1;
+       }
+
+       if (wnm_sleep) {
+               sm->igtk_wnm_sleep.igtk_len = len;
+               os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk,
+                         sm->igtk_wnm_sleep.igtk_len);
+       } else {
+               sm->igtk.igtk_len = len;
+               os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
+       }
+
+       return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
 static int ieee80211w_set_keys(struct wpa_sm *sm,
                               struct wpa_eapol_ie_parse *ie)
 {
@@ -946,30 +1087,14 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
        if (ie->igtk) {
                size_t len;
                const struct wpa_igtk_kde *igtk;
-               u16 keyidx;
+
                len = wpa_cipher_key_len(sm->mgmt_group_cipher);
                if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
                        return -1;
+
                igtk = (const struct wpa_igtk_kde *) ie->igtk;
-               keyidx = WPA_GET_LE16(igtk->keyid);
-               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
-                       "pn %02x%02x%02x%02x%02x%02x",
-                       keyidx, MAC2STR(igtk->pn));
-               wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
-                               igtk->igtk, len);
-               if (keyidx > 4095) {
-                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-                               "WPA: Invalid IGTK KeyID %d", keyidx);
+               if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0)
                        return -1;
-               }
-               if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
-                                  broadcast_ether_addr,
-                                  keyidx, 0, igtk->pn, sizeof(igtk->pn),
-                                  igtk->igtk, len) < 0) {
-                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-                               "WPA: Failed to configure IGTK to the driver");
-                       return -1;
-               }
        }
 
        return 0;
@@ -1231,7 +1356,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
        struct wpa_eapol_key *reply;
        u8 *rbuf, *key_mic;
 
-       mic_len = wpa_mic_len(sm->key_mgmt);
+       mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
        hdrlen = sizeof(*reply) + mic_len + 2;
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
                                  hdrlen, &rlen, (void *) &reply);
@@ -1332,6 +1457,26 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        }
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm)) {
+               struct wpa_channel_info ci;
+
+               if (wpa_sm_channel_info(sm, &ci) != 0) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "Failed to get channel info to validate received OCI in EAPOL-Key 3/4");
+                       return;
+               }
+
+               if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+                                        channel_width_to_int(ci.chanwidth),
+                                        ci.seg1_idx) != 0) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
+                               ocv_errorstr);
+                       return;
+               }
+       }
+#endif /* CONFIG_OCV */
+
        if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
                                       &sm->ptk) < 0) {
                goto failed;
@@ -1356,8 +1501,11 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 
        if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
-               wpa_supplicant_key_neg_complete(sm, sm->bssid,
-                                               key_info & WPA_KEY_INFO_SECURE);
+               /* No GTK to be set to the driver */
+       } else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: No GTK KDE included in EAPOL-Key msg 3/4");
+               goto failed;
        } else if (ie.gtk &&
            wpa_supplicant_pairwise_gtk(sm, key,
                                        ie.gtk, ie.gtk_len, key_info) < 0) {
@@ -1372,10 +1520,20 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                goto failed;
        }
 
+       if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
+               wpa_supplicant_key_neg_complete(sm, sm->bssid,
+                                               key_info & WPA_KEY_INFO_SECURE);
+
        if (ie.gtk)
                wpa_sm_set_rekey_offload(sm);
 
-       if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+       /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
+        * calculated only after KCK has been derived. Though, do not replace an
+        * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID)
+        * to avoid unnecessary changes of PMKID while continuing to use the
+        * same PMK. */
+       if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+           !sm->cur_pmksa) {
                struct rsn_pmksa_cache_entry *sa;
 
                sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
@@ -1419,6 +1577,26 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
        }
        maxkeylen = gd->gtk_len = ie.gtk_len - 2;
 
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm)) {
+               struct wpa_channel_info ci;
+
+               if (wpa_sm_channel_info(sm, &ci) != 0) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+                       return -1;
+               }
+
+               if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+                                        channel_width_to_int(ci.chanwidth),
+                                        ci.seg1_idx) != 0) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
+                               ocv_errorstr);
+                       return -1;
+               }
+       }
+#endif /* CONFIG_OCV */
+
        if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
                                              gd->gtk_len, maxkeylen,
                                              &gd->key_rsc_len, &gd->alg))
@@ -1539,11 +1717,17 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
        size_t mic_len, hdrlen, rlen;
        struct wpa_eapol_key *reply;
        u8 *rbuf, *key_mic;
+       size_t kde_len = 0;
+
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm))
+               kde_len = OCV_OCI_KDE_LEN;
+#endif /* CONFIG_OCV */
 
-       mic_len = wpa_mic_len(sm->key_mgmt);
+       mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
        hdrlen = sizeof(*reply) + mic_len + 2;
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-                                 hdrlen, &rlen, (void *) &reply);
+                                 hdrlen + kde_len, &rlen, (void *) &reply);
        if (rbuf == NULL)
                return -1;
 
@@ -1554,6 +1738,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
        key_info |= ver | WPA_KEY_INFO_SECURE;
        if (mic_len)
                key_info |= WPA_KEY_INFO_MIC;
+       else
+               key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
        WPA_PUT_BE16(reply->key_info, key_info);
        if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
                WPA_PUT_BE16(reply->key_length, 0);
@@ -1563,7 +1749,27 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
                  WPA_REPLAY_COUNTER_LEN);
 
        key_mic = (u8 *) (reply + 1);
-       WPA_PUT_BE16(key_mic + mic_len, 0);
+       WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data Length */
+
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm)) {
+               struct wpa_channel_info ci;
+               u8 *pos;
+
+               if (wpa_sm_channel_info(sm, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to get channel info for OCI element in EAPOL-Key 2/2");
+                       os_free(rbuf);
+                       return -1;
+               }
+
+               pos = key_mic + mic_len + 2; /* Key Data */
+               if (ocv_insert_oci_kde(&ci, &pos) < 0) {
+                       os_free(rbuf);
+                       return -1;
+               }
+       }
+#endif /* CONFIG_OCV */
 
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
        return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
@@ -1582,7 +1788,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
        struct wpa_gtk_data gd;
        const u8 *key_rsc;
 
-       if (!sm->msg_3_of_4_ok) {
+       if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: Group Key Handshake started prior to completion of 4-way handshake");
                goto failed;
@@ -1615,7 +1821,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
        if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
                key_rsc = null_rsc;
 
-       if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) ||
+       if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
            wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
                goto failed;
        os_memset(&gd, 0, sizeof(gd));
@@ -1649,36 +1855,62 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
 {
        u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
        int ok = 0;
-       size_t mic_len = wpa_mic_len(sm->key_mgmt);
+       size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
 
        os_memcpy(mic, key + 1, mic_len);
        if (sm->tptk_set) {
                os_memset(key + 1, 0, mic_len);
-               wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
-                                 ver, buf, len, (u8 *) (key + 1));
-               if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+               if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len,
+                                     sm->key_mgmt,
+                                     ver, buf, len, (u8 *) (key + 1)) < 0 ||
+                   os_memcmp_const(mic, key + 1, mic_len) != 0) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Invalid EAPOL-Key MIC "
                                "when using TPTK - ignoring TPTK");
+#ifdef TEST_FUZZ
+                       wpa_printf(MSG_INFO,
+                                  "TEST: Ignore Key MIC failure for fuzz testing");
+                       goto continue_fuzz;
+#endif /* TEST_FUZZ */
                } else {
+#ifdef TEST_FUZZ
+               continue_fuzz:
+#endif /* TEST_FUZZ */
                        ok = 1;
                        sm->tptk_set = 0;
                        sm->ptk_set = 1;
                        os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
                        os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+                       /*
+                        * This assures the same TPTK in sm->tptk can never be
+                        * copied twice to sm->ptk as the new PTK. In
+                        * combination with the installed flag in the wpa_ptk
+                        * struct, this assures the same PTK is only installed
+                        * once.
+                        */
+                       sm->renew_snonce = 1;
                }
        }
 
        if (!ok && sm->ptk_set) {
                os_memset(key + 1, 0, mic_len);
-               wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
-                                 ver, buf, len, (u8 *) (key + 1));
-               if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+               if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len,
+                                     sm->key_mgmt,
+                                     ver, buf, len, (u8 *) (key + 1)) < 0 ||
+                   os_memcmp_const(mic, key + 1, mic_len) != 0) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Invalid EAPOL-Key MIC - "
                                "dropping packet");
+#ifdef TEST_FUZZ
+                       wpa_printf(MSG_INFO,
+                                  "TEST: Ignore Key MIC failure for fuzz testing");
+                       goto continue_fuzz2;
+#endif /* TEST_FUZZ */
                        return -1;
                }
+#ifdef TEST_FUZZ
+       continue_fuzz2:
+#endif /* TEST_FUZZ */
                ok = 1;
        }
 
@@ -1720,6 +1952,8 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
                return -1;
 #else /* CONFIG_NO_RC4 */
                u8 ek[32];
+
+               wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
                os_memcpy(ek, key->key_iv, 16);
                os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
                if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
@@ -1732,9 +1966,12 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
 #endif /* CONFIG_NO_RC4 */
        } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
                   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
-                  sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
-                  wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+                  wpa_use_aes_key_wrap(sm->key_mgmt)) {
                u8 *buf;
+
+               wpa_printf(MSG_DEBUG,
+                          "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)",
+                          (unsigned int) sm->ptk.kek_len);
                if (*key_data_len < 8 || *key_data_len % 8) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Unsupported AES-WRAP len %u",
@@ -1748,14 +1985,25 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
                                "WPA: No memory for AES-UNWRAP buffer");
                        return -1;
                }
+#ifdef TEST_FUZZ
+               os_memset(buf, 0x11, *key_data_len);
+#endif /* TEST_FUZZ */
                if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
                               key_data, buf)) {
+#ifdef TEST_FUZZ
+                       wpa_printf(MSG_INFO,
+                                  "TEST: Ignore AES unwrap failure for fuzz testing");
+                       goto continue_fuzz;
+#endif /* TEST_FUZZ */
                        bin_clear_free(buf, *key_data_len);
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: AES unwrap failed - "
                                "could not decrypt EAPOL-Key key data");
                        return -1;
                }
+#ifdef TEST_FUZZ
+       continue_fuzz:
+#endif /* TEST_FUZZ */
                os_memcpy(key_data, buf, *key_data_len);
                bin_clear_free(buf, *key_data_len);
                WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
@@ -1916,7 +2164,6 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        u16 key_info, ver;
        u8 *tmp = NULL;
        int ret = -1;
-       struct wpa_peerkey *peerkey = NULL;
        u8 *mic, *key_data;
        size_t mic_len, keyhdrlen;
 
@@ -1924,7 +2171,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R */
 
-       mic_len = wpa_mic_len(sm->key_mgmt);
+       mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
        keyhdrlen = sizeof(*key) + mic_len + 2;
 
        if (len < sizeof(*hdr) + keyhdrlen) {
@@ -2007,25 +2254,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
            ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
            ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
-           !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
-           !wpa_key_mgmt_fils(sm->key_mgmt) &&
-           sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
+           !wpa_use_akm_defined(sm->key_mgmt)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: Unsupported EAPOL-Key descriptor version %d",
                        ver);
                goto out;
        }
 
-       if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
-           ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
-               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-                       "OSEN: Unsupported EAPOL-Key descriptor version %d",
-                       ver);
-               goto out;
-       }
-
-       if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
-            wpa_key_mgmt_fils(sm->key_mgmt)) &&
+       if (wpa_use_akm_defined(sm->key_mgmt) &&
            ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -2036,7 +2272,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->key_mgmt)) {
                /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
-               if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+               if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+                   !wpa_use_akm_defined(sm->key_mgmt)) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                                "FT: AP did not use AES-128-CMAC");
                        goto out;
@@ -2046,9 +2283,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 #ifdef CONFIG_IEEE80211W
        if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
                if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
-                   sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
-                   !wpa_key_mgmt_fils(sm->key_mgmt) &&
-                   !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+                   !wpa_use_akm_defined(sm->key_mgmt)) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                                "WPA: AP did not use the "
                                "negotiated AES-128-CMAC");
@@ -2057,8 +2292,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        } else
 #endif /* CONFIG_IEEE80211W */
        if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
-           !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
-           !wpa_key_mgmt_fils(sm->key_mgmt) &&
+           !wpa_use_akm_defined(sm->key_mgmt) &&
            ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: CCMP is used, but EAPOL-Key "
@@ -2078,7 +2312,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                } else
                        goto out;
        } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
-                  !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+                  !wpa_use_akm_defined(sm->key_mgmt) &&
                   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: GCMP is used, but EAPOL-Key "
@@ -2086,44 +2320,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                goto out;
        }
 
-#ifdef CONFIG_PEERKEY
-       for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-               if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
-                       break;
-       }
-
-       if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
-               if (!peerkey->initiator && peerkey->replay_counter_set &&
-                   os_memcmp(key->replay_counter, peerkey->replay_counter,
-                             WPA_REPLAY_COUNTER_LEN) <= 0) {
-                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-                               "RSN: EAPOL-Key Replay Counter did not "
-                               "increase (STK) - dropping packet");
-                       goto out;
-               } else if (peerkey->initiator) {
-                       u8 _tmp[WPA_REPLAY_COUNTER_LEN];
-                       os_memcpy(_tmp, key->replay_counter,
-                                 WPA_REPLAY_COUNTER_LEN);
-                       inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
-                       if (os_memcmp(_tmp, peerkey->replay_counter,
-                                     WPA_REPLAY_COUNTER_LEN) != 0) {
-                               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-                                       "RSN: EAPOL-Key Replay "
-                                       "Counter did not match (STK) - "
-                                       "dropping packet");
-                               goto out;
-                       }
-               }
-       }
-
-       if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
-               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-                       "RSN: Ack bit in key_info from STK peer");
-               goto out;
-       }
-#endif /* CONFIG_PEERKEY */
-
-       if (!peerkey && sm->rx_replay_counter_set &&
+       if (sm->rx_replay_counter_set &&
            os_memcmp(key->replay_counter, sm->rx_replay_counter,
                      WPA_REPLAY_COUNTER_LEN) <= 0) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -2132,11 +2329,13 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                goto out;
        }
 
-       if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
-#ifdef CONFIG_PEERKEY
-           && (peerkey == NULL || !peerkey->initiator)
-#endif /* CONFIG_PEERKEY */
-               ) {
+       if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "WPA: Unsupported SMK bit in key_info");
+               goto out;
+       }
+
+       if (!(key_info & WPA_KEY_INFO_ACK)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: No Ack bit in key_info");
                goto out;
@@ -2148,17 +2347,10 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                goto out;
        }
 
-       if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+       if ((key_info & WPA_KEY_INFO_MIC) &&
            wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
                goto out;
 
-#ifdef CONFIG_PEERKEY
-       if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-           peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp,
-                                        data_len))
-               goto out;
-#endif /* CONFIG_PEERKEY */
-
 #ifdef CONFIG_FILS
        if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
                if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
@@ -2168,6 +2360,17 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 
        if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
            (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
+               /*
+                * Only decrypt the Key Data field if the frame's authenticity
+                * was verified. When using AES-SIV (FILS), the MIC flag is not
+                * set, so this check should only be performed if mic_len != 0
+                * which is the case in this code branch.
+                */
+               if (!(key_info & WPA_KEY_INFO_MIC)) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Ignore EAPOL-Key with encrypted but unauthenticated data");
+                       goto out;
+               }
                if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
                                                    ver, key_data,
                                                    &key_data_len))
@@ -2181,12 +2384,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                                "non-zero key index");
                        goto out;
                }
-               if (peerkey) {
-                       /* PeerKey 4-Way Handshake */
-                       peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
-                                             key_data, key_data_len);
-               } else if (key_info & (WPA_KEY_INFO_MIC |
-                                      WPA_KEY_INFO_ENCR_KEY_DATA)) {
+               if (key_info & (WPA_KEY_INFO_MIC |
+                               WPA_KEY_INFO_ENCR_KEY_DATA)) {
                        /* 3/4 4-Way Handshake */
                        wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
                                                      key_data_len);
@@ -2196,10 +2395,6 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                                                      ver, key_data,
                                                      key_data_len);
                }
-       } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
-               /* PeerKey SMK Handshake */
-               peerkey_rx_eapol_smk(sm, src_addr, key, key_data, key_data_len,
-                                    key_info, ver);
        } else {
                if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
                    (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
@@ -2384,6 +2579,7 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
        }
 
        if (deauth) {
+               sm->pmk_len = 0;
                os_memset(sm->pmk, 0, sizeof(sm->pmk));
                wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
        }
@@ -2441,16 +2637,24 @@ void wpa_sm_deinit(struct wpa_sm *sm)
        os_free(sm->ap_rsn_ie);
        wpa_sm_drop_sa(sm);
        os_free(sm->ctx);
-       peerkey_deinit(sm);
 #ifdef CONFIG_IEEE80211R
        os_free(sm->assoc_resp_ies);
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_TESTING_OPTIONS
        wpabuf_free(sm->test_assoc_ie);
 #endif /* CONFIG_TESTING_OPTIONS */
+#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 */
+#ifdef CONFIG_DPP2
+       wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
        os_free(sm);
 }
 
@@ -2465,7 +2669,7 @@ void wpa_sm_deinit(struct wpa_sm *sm)
  */
 void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
 {
-       int clear_ptk = 1;
+       int clear_keys = 1;
 
        if (sm == NULL)
                return;
@@ -2491,7 +2695,7 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
                /* Prepare for the next transition */
                wpa_ft_prepare_auth_request(sm, NULL);
 
-               clear_ptk = 0;
+               clear_keys = 0;
        }
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_FILS
@@ -2501,11 +2705,11 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
                 * AUTHENTICATED state to get the EAPOL port Authorized.
                 */
                wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
-               clear_ptk = 0;
+               clear_keys = 0;
        }
 #endif /* CONFIG_FILS */
 
-       if (clear_ptk) {
+       if (clear_keys) {
                /*
                 * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
                 * this is not part of a Fast BSS Transition.
@@ -2515,6 +2719,12 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
                os_memset(&sm->ptk, 0, sizeof(sm->ptk));
                sm->tptk_set = 0;
                os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+               os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+               os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+#ifdef CONFIG_IEEE80211W
+               os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+               os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+#endif /* CONFIG_IEEE80211W */
        }
 
 #ifdef CONFIG_TDLS
@@ -2538,7 +2748,6 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 {
        eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
        eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
-       peerkey_deinit(sm);
        rsn_preauth_deinit(sm);
        pmksa_cache_clear_current(sm);
        if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
@@ -2549,11 +2758,15 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 #ifdef CONFIG_FILS
        sm->fils_completed = 0;
 #endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+       sm->ft_reassoc_completed = 0;
+#endif /* CONFIG_IEEE80211R */
 
        /* Keys are not needed in the WPA state machine anymore */
        wpa_sm_drop_sa(sm);
 
        sm->msg_3_of_4_ok = 0;
+       os_memset(sm->bssid, 0, ETH_ALEN);
 }
 
 
@@ -2573,6 +2786,8 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
        if (sm == NULL)
                return;
 
+       wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data",
+                       pmk, pmk_len);
        sm->pmk_len = pmk_len;
        os_memcpy(sm->pmk, pmk, pmk_len);
 
@@ -2603,11 +2818,15 @@ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
                return;
 
        if (sm->cur_pmksa) {
+               wpa_hexdump_key(MSG_DEBUG,
+                               "WPA: Set PMK based on current PMKSA",
+                               sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len);
                sm->pmk_len = sm->cur_pmksa->pmk_len;
                os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
        } else {
-               sm->pmk_len = PMK_LEN;
-               os_memset(sm->pmk, 0, PMK_LEN);
+               wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK");
+               sm->pmk_len = 0;
+               os_memset(sm->pmk, 0, PMK_LEN_MAX);
        }
 }
 
@@ -2655,7 +2874,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
 
        if (config) {
                sm->network_ctx = config->network_ctx;
-               sm->peerkey_enabled = config->peerkey_enabled;
                sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
                sm->proactive_key_caching = config->proactive_key_caching;
                sm->eap_workaround = config->eap_workaround;
@@ -2679,7 +2897,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
 #endif /* CONFIG_FILS */
        } else {
                sm->network_ctx = NULL;
-               sm->peerkey_enabled = 0;
                sm->allowed_pairwise_cipher = 0;
                sm->proactive_key_caching = 0;
                sm->eap_workaround = 0;
@@ -2789,6 +3006,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
        case WPA_PARAM_MFP:
                sm->mfp = value;
                break;
+       case WPA_PARAM_OCV:
+               sm->ocv = value;
+               break;
        default:
                break;
        }
@@ -2832,9 +3052,12 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
                    >= 0 &&
                    rsn.capabilities & (WPA_CAPABILITY_MFPR |
                                        WPA_CAPABILITY_MFPC)) {
-                       ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+                       ret = os_snprintf(pos, end - pos, "pmf=%d\n"
+                                         "mgmt_group_cipher=%s\n",
                                          (rsn.capabilities &
-                                          WPA_CAPABILITY_MFPR) ? 2 : 1);
+                                          WPA_CAPABILITY_MFPR) ? 2 : 1,
+                                         wpa_cipher_txt(
+                                                 sm->mgmt_group_cipher));
                        if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
@@ -2860,6 +3083,19 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm)
 }
 
 
+int wpa_sm_ocv_enabled(struct wpa_sm *sm)
+{
+       struct wpa_ie_data rsn;
+
+       if (!sm->ocv || !sm->ap_rsn_ie)
+               return 0;
+
+       return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len,
+                                   &rsn) >= 0 &&
+               (rsn.capabilities & WPA_CAPABILITY_OCVC);
+}
+
+
 /**
  * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3063,18 +3299,45 @@ wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
 }
 
 
+void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+                           const u8 *pmkid, const u8 *bssid,
+                           const u8 *fils_cache_id)
+{
+       sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
+                                       bssid, sm->own_addr, sm->network_ctx,
+                                       sm->key_mgmt, fils_cache_id);
+}
+
+
+int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
+                       const void *network_ctx)
+{
+       return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
+}
+
+
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
        sm->ptk_set = 0;
        sm->tptk_set = 0;
+       sm->pmk_len = 0;
        os_memset(sm->pmk, 0, sizeof(sm->pmk));
        os_memset(&sm->ptk, 0, sizeof(sm->ptk));
        os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+       os_memset(&sm->gtk, 0, sizeof(sm->gtk));
+       os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
+#ifdef CONFIG_IEEE80211W
+       os_memset(&sm->igtk, 0, sizeof(sm->igtk));
+       os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
+#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
        os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+       sm->xxkey_len = 0;
        os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+       sm->pmk_r0_len = 0;
        os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+       sm->pmk_r1_len = 0;
 #endif /* CONFIG_IEEE80211R */
 }
 
@@ -3134,7 +3397,7 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 
                wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
                                gd.gtk, gd.gtk_len);
-               if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+               if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) {
                        os_memset(&gd, 0, sizeof(gd));
                        wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
                                   "WNM mode");
@@ -3143,29 +3406,11 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
                os_memset(&gd, 0, sizeof(gd));
 #ifdef CONFIG_IEEE80211W
        } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
-               struct wpa_igtk_kde igd;
-               u16 keyidx;
-
-               os_memset(&igd, 0, sizeof(igd));
-               keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
-               os_memcpy(igd.keyid, buf + 2, 2);
-               os_memcpy(igd.pn, buf + 4, 6);
-
-               keyidx = WPA_GET_LE16(igd.keyid);
-               os_memcpy(igd.igtk, buf + 10, keylen);
-
-               wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
-                               igd.igtk, keylen);
-               if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
-                                  broadcast_ether_addr,
-                                  keyidx, 0, igd.pn, sizeof(igd.pn),
-                                  igd.igtk, keylen) < 0) {
-                       wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
-                                  "WNM mode");
-                       os_memset(&igd, 0, sizeof(igd));
+               const struct wpa_igtk_kde *igtk;
+
+               igtk = (const struct wpa_igtk_kde *) (buf + 2);
+               if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
                        return -1;
-               }
-               os_memset(&igd, 0, sizeof(igd));
 #endif /* CONFIG_IEEE80211W */
        } else {
                wpa_printf(MSG_DEBUG, "Unknown element id");
@@ -3177,27 +3422,6 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 #endif /* CONFIG_WNM */
 
 
-#ifdef CONFIG_PEERKEY
-int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
-                           const u8 *buf, size_t len)
-{
-       struct wpa_peerkey *peerkey;
-
-       for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
-               if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
-                       break;
-       }
-
-       if (!peerkey)
-               return 0;
-
-       wpa_sm_rx_eapol(sm, src_addr, buf, len);
-
-       return 1;
-}
-#endif /* CONFIG_PEERKEY */
-
-
 #ifdef CONFIG_P2P
 
 int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
@@ -3242,20 +3466,35 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
 
 
 #ifdef CONFIG_TESTING_OPTIONS
+
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
 {
        wpabuf_free(sm->test_assoc_ie);
        sm->test_assoc_ie = buf;
 }
+
+
+const u8 * wpa_sm_get_anonce(struct wpa_sm *sm)
+{
+       return sm->anonce;
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
+unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm)
+{
+       return sm->key_mgmt;
+}
+
+
 #ifdef CONFIG_FILS
 
-struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
 {
        struct wpabuf *buf = NULL;
        struct wpabuf *erp_msg;
+       struct wpabuf *pub = NULL;
 
        erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
        if (!erp_msg && !sm->cur_pmksa) {
@@ -3283,7 +3522,28 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm)
        wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
                    sm->fils_session, FILS_SESSION_LEN);
 
-       buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+#ifdef CONFIG_FILS_SK_PFS
+       sm->fils_dh_group = dh_group;
+       if (dh_group) {
+               crypto_ecdh_deinit(sm->fils_ecdh);
+               sm->fils_ecdh = crypto_ecdh_init(dh_group);
+               if (!sm->fils_ecdh) {
+                       wpa_printf(MSG_INFO,
+                                  "FILS: Could not initialize ECDH with group %d",
+                                  dh_group);
+                       goto fail;
+               }
+               pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+               if (!pub)
+                       goto fail;
+               wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)",
+                               pub);
+               sm->fils_dh_elem_len = wpabuf_len(pub);
+       }
+#endif /* CONFIG_FILS_SK_PFS */
+
+       buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len +
+                          (pub ? wpabuf_len(pub) : 0));
        if (!buf)
                goto fail;
 
@@ -3295,16 +3555,31 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm)
        /* Status Code */
        wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 
-       /* 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 (dh_group) {
+               /* Finite Cyclic Group */
+               wpabuf_put_le16(buf, dh_group);
+               /* Element */
+               wpabuf_put_buf(buf, pub);
+       }
+#endif /* CONFIG_FILS_SK_PFS */
 
        /* RSNE */
        wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
                    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
        wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 
-       /* TODO: MDE when using FILS for FT initial association */
-       /* TODO: FTE when using FILS for FT initial association */
+       if (md) {
+               /* MDE when using FILS for FT initial association */
+               struct rsn_mdie *mdie;
+
+               wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN);
+               wpabuf_put_u8(buf, sizeof(*mdie));
+               mdie = wpabuf_put(buf, sizeof(*mdie));
+               os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
+               mdie->ft_capab = 0;
+       }
 
        /* FILS Nonce */
        wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
@@ -3341,6 +3616,7 @@ struct wpabuf * fils_build_auth(struct wpa_sm *sm)
 
 fail:
        wpabuf_free(erp_msg);
+       wpabuf_free(pub);
        return buf;
 }
 
@@ -3355,6 +3631,12 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        u8 ick[FILS_ICK_MAX_LEN];
        size_t ick_len;
        int res;
+       struct wpabuf *dh_ss = NULL;
+       const u8 *g_sta = NULL;
+       size_t g_sta_len = 0;
+       const u8 *g_ap = NULL;
+       size_t g_ap_len = 0;
+       struct wpabuf *pub = NULL;
 
        os_memcpy(sm->bssid, bssid, ETH_ALEN);
 
@@ -3363,13 +3645,55 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        pos = data;
        end = data + len;
 
-       /* 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 (sm->fils_dh_group) {
+               u16 group;
+
+               /* Using FILS PFS */
+
+               /* Finite Cyclic Group */
+               if (end - pos < 2) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: No room for Finite Cyclic Group");
+                       goto fail;
+               }
+               group = WPA_GET_LE16(pos);
+               pos += 2;
+               if (group != sm->fils_dh_group) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)",
+                                  group, sm->fils_dh_group);
+                       goto fail;
+               }
+
+               /* Element */
+               if ((size_t) (end - pos) < sm->fils_dh_elem_len) {
+                       wpa_printf(MSG_DEBUG, "FILS: No room for Element");
+                       goto fail;
+               }
+
+               if (!sm->fils_ecdh) {
+                       wpa_printf(MSG_DEBUG, "FILS: No ECDH state available");
+                       goto fail;
+               }
+               dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos,
+                                               sm->fils_dh_elem_len);
+               if (!dh_ss) {
+                       wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
+                       goto fail;
+               }
+               wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss);
+               g_ap = pos;
+               g_ap_len = sm->fils_dh_elem_len;
+               pos += sm->fils_dh_elem_len;
+       }
+#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) {
                wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
-               return -1;
+               goto fail;
        }
 
        /* RSNE */
@@ -3379,18 +3703,66 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
            wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
                                 &rsn) < 0) {
                wpa_printf(MSG_DEBUG, "FILS: No RSN element");
-               return -1;
+               goto fail;
        }
 
        if (!elems.fils_nonce) {
                wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
-               return -1;
+               goto fail;
        }
        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 */
+#ifdef CONFIG_IEEE80211R
+       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,
+                                    wpa_key_mgmt_sha384(sm->key_mgmt)) < 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;
+       }
+#endif /* CONFIG_IEEE80211R */
 
        /* PMKID List */
        if (rsn.pmkid && rsn.num_pmkid > 0) {
@@ -3399,7 +3771,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
 
                if (rsn.num_pmkid != 1) {
                        wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
-                       return -1;
+                       goto fail;
                }
                wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
                if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
@@ -3407,7 +3779,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                        wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
                        wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
                                    sm->cur_pmksa->pmkid, PMKID_LEN);
-                       return -1;
+                       goto fail;
                }
                wpa_printf(MSG_DEBUG,
                           "FILS: Matching PMKID - continue using PMKSA caching");
@@ -3422,7 +3794,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        /* FILS Session */
        if (!elems.fils_session) {
                wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
-               return -1;
+               goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
                    FILS_SESSION_LEN);
@@ -3431,7 +3803,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
                wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
                            sm->fils_session, FILS_SESSION_LEN);
-               return -1;
+               goto fail;
        }
 
        /* FILS Wrapped Data */
@@ -3445,7 +3817,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
                                            elems.fils_wrapped_data_len);
                if (eapol_sm_failed(sm->eapol))
-                       return -1;
+                       goto fail;
 
                rmsk_len = ERP_MAX_KEY_LEN;
                res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
@@ -3454,18 +3826,26 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
                        res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
                }
                if (res)
-                       return -1;
+                       goto fail;
 
                res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
-                                      sm->fils_nonce, sm->fils_anonce, NULL, 0,
+                                      sm->fils_nonce, sm->fils_anonce,
+                                      dh_ss ? wpabuf_head(dh_ss) : NULL,
+                                      dh_ss ? wpabuf_len(dh_ss) : 0,
                                       sm->pmk, &sm->pmk_len);
                os_memset(rmsk, 0, sizeof(rmsk));
+
+               /* Don't use DHss in PTK derivation if PMKSA caching is not
+                * used. */
+               wpabuf_clear_free(dh_ss);
+               dh_ss = NULL;
+
                if (res)
-                       return -1;
+                       goto fail;
 
                if (!sm->fils_erp_pmkid_set) {
                        wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
-                       return -1;
+                       goto fail;
                }
                wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
                            PMKID_LEN);
@@ -3480,31 +3860,174 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
        if (!sm->cur_pmksa) {
                wpa_printf(MSG_DEBUG,
                           "FILS: No remaining options to continue FILS authentication");
-               return -1;
+               goto fail;
        }
 
        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) <
-           0) {
+                           sm->fils_nonce, sm->fils_anonce,
+                           dh_ss ? wpabuf_head(dh_ss) : NULL,
+                           dh_ss ? wpabuf_len(dh_ss) : 0,
+                           &sm->ptk, ick, &ick_len,
+                           sm->key_mgmt, sm->pairwise_cipher,
+                           sm->fils_ft, &sm->fils_ft_len) < 0) {
                wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
-               return -1;
+               goto fail;
        }
+
+       wpabuf_clear_free(dh_ss);
+       dh_ss = NULL;
+
        sm->ptk_set = 1;
        sm->tptk_set = 0;
        os_memset(&sm->tptk, 0, sizeof(sm->tptk));
 
+#ifdef CONFIG_FILS_SK_PFS
+       if (sm->fils_dh_group) {
+               if (!sm->fils_ecdh) {
+                       wpa_printf(MSG_INFO, "FILS: ECDH not initialized");
+                       goto fail;
+               }
+               pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
+               if (!pub)
+                       goto fail;
+               wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub);
+               g_sta = wpabuf_head(pub);
+               g_sta_len = wpabuf_len(pub);
+               if (!g_ap) {
+                       wpa_printf(MSG_INFO, "FILS: gAP not available");
+                       goto fail;
+               }
+               wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
+       }
+#endif /* CONFIG_FILS_SK_PFS */
+
        res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
                               sm->fils_anonce, sm->own_addr, sm->bssid,
-                              NULL, 0, NULL, 0, /* TODO: SK+PFS */
+                              g_sta, g_sta_len, g_ap, g_ap_len,
                               sm->key_mgmt, sm->fils_key_auth_sta,
                               sm->fils_key_auth_ap,
                               &sm->fils_key_auth_len);
+       wpabuf_free(pub);
        os_memset(ick, 0, sizeof(ick));
        return res;
+fail:
+       wpabuf_free(pub);
+       wpabuf_clear_free(dh_ss);
+       return -1;
 }
 
 
+#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;
+       int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
+
+       /* 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 */
+       if (sm->ocv)
+               capab |= WPA_CAPABILITY_OCVC;
+       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, use_sha384) < 0) {
+               wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
+               return -1;
+       }
+       sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
+       wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0",
+                       sm->pmk_r0, sm->pmk_r0_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,
+                                  sm->pmk_r1_name, use_sha384) < 0) {
+               wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+                   WPA_PMK_NAME_LEN);
+       os_memcpy(pos, sm->pmk_r1_name, 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,
@@ -3516,12 +4039,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 */
@@ -3572,6 +4113,26 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
 
        /* TODO: FILS IP Address Assignment */
 
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm)) {
+               struct wpa_channel_info ci;
+               u8 *pos;
+
+               if (wpa_sm_channel_info(sm, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "FILS: Failed to get channel info for OCI element");
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+
+               pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN);
+               if (ocv_insert_extended_oci(&ci, pos) < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_OCV */
+
        wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
 
        *kek = sm->ptk.kek;
@@ -3674,6 +4235,12 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
                return -1;
        }
 
+       if (sm->fils_completed) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission");
+               return -1;
+       }
+
        wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
                    resp, len);
 
@@ -3729,6 +4296,43 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
                goto fail;
        }
 
+#ifdef CONFIG_OCV
+       if (wpa_sm_ocv_enabled(sm)) {
+               struct wpa_channel_info ci;
+
+               if (wpa_sm_channel_info(sm, &ci) != 0) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to get channel info to validate received OCI in FILS (Re)Association Response frame");
+                       goto fail;
+               }
+
+               if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+                                        channel_width_to_int(ci.chanwidth),
+                                        ci.seg1_idx) != 0) {
+                       wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+                       goto fail;
+               }
+       }
+#endif /* CONFIG_OCV */
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+               struct wpa_ie_data rsn;
+
+               /* Check that PMKR1Name derived by the AP matches */
+               if (!elems.rsn_ie ||
+                   wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+                                        &rsn) < 0 ||
+                   !rsn.pmkid || rsn.num_pmkid != 1 ||
+                   os_memcmp(rsn.pmkid, sm->pmk_r1_name,
+                             WPA_PMK_NAME_LEN) != 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "FILS+FT: No RSNE[PMKR1Name] match in AssocResp");
+                       goto fail;
+               }
+       }
+#endif /* CONFIG_IEEE80211R */
+
        /* Key Delivery */
        if (!elems.key_delivery) {
                wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
@@ -3765,7 +4369,7 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
        os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
 
        wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
-       if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery) < 0) {
+       if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) {
                wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
                goto fail;
        }
@@ -3777,6 +4381,11 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
 
        alg = wpa_cipher_to_alg(sm->pairwise_cipher);
        keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+       if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+               wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu",
+                          keylen, (long unsigned int) sm->ptk.tk_len);
+               goto fail;
+       }
        rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
        wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
                        sm->ptk.tk, keylen);
@@ -3793,6 +4402,8 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
         * takes care of association frame encryption/decryption. */
        /* TK is not needed anymore in supplicant */
        os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+       sm->ptk.tk_len = 0;
+       sm->ptk.installed = 1;
 
        /* FILS HLP Container */
        fils_process_hlp_container(sm, ie_start, end - ie_start);
@@ -3807,6 +4418,13 @@ fail:
        return -1;
 }
 
+
+void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set)
+{
+       if (sm)
+               sm->fils_completed = !!set;
+}
+
 #endif /* CONFIG_FILS */
 
 
@@ -3822,15 +4440,27 @@ int wpa_fils_is_completed(struct wpa_sm *sm)
 
 #ifdef CONFIG_OWE
 
-struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm)
+struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group)
 {
        struct wpabuf *ie = NULL, *pub = NULL;
+       size_t prime_len;
+
+       if (group == 19)
+               prime_len = 32;
+       else if (group == 20)
+               prime_len = 48;
+       else if (group == 21)
+               prime_len = 66;
+       else
+               return NULL;
 
        crypto_ecdh_deinit(sm->owe_ecdh);
-       sm->owe_ecdh = crypto_ecdh_init(OWE_DH_GROUP);
+       sm->owe_ecdh = crypto_ecdh_init(group);
        if (!sm->owe_ecdh)
                goto fail;
+       sm->owe_group = group;
        pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
+       pub = wpabuf_zeropad(pub, prime_len);
        if (!pub)
                goto fail;
 
@@ -3840,7 +4470,7 @@ struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm)
        wpabuf_put_u8(ie, WLAN_EID_EXTENSION);
        wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub));
        wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM);
-       wpabuf_put_le16(ie, OWE_DH_GROUP);
+       wpabuf_put_le16(ie, group);
        wpabuf_put_buf(ie, pub);
        wpabuf_free(pub);
        wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element",
@@ -3855,29 +4485,46 @@ fail:
 }
 
 
-int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *resp_ies,
-                          size_t resp_ies_len)
+int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
+                          const u8 *resp_ies, size_t resp_ies_len)
 {
        struct ieee802_11_elems elems;
        u16 group;
        struct wpabuf *secret, *pub, *hkey;
        int res;
-       u8 prk[SHA256_MAC_LEN], pmkid[SHA256_MAC_LEN];
+       u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
        const char *info = "OWE Key Generation";
        const u8 *addr[2];
        size_t len[2];
+       size_t hash_len, prime_len;
+       struct wpa_ie_data data;
 
        if (!resp_ies ||
            ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) ==
-           ParseFailed ||
-           !elems.owe_dh) {
+           ParseFailed) {
+               wpa_printf(MSG_INFO,
+                          "OWE: Could not parse Association Response frame elements");
+               return -1;
+       }
+
+       if (sm->cur_pmksa && elems.rsn_ie &&
+           wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
+                                &data) == 0 &&
+           data.num_pmkid == 1 && data.pmkid &&
+           os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) {
+               wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching");
+               wpa_sm_set_pmk_from_pmksa(sm);
+               return 0;
+       }
+
+       if (!elems.owe_dh) {
                wpa_printf(MSG_INFO,
                           "OWE: No Diffie-Hellman Parameter element found in Association Response frame");
                return -1;
        }
 
        group = WPA_GET_LE16(elems.owe_dh);
-       if (group != OWE_DH_GROUP) {
+       if (group != sm->owe_group) {
                wpa_printf(MSG_INFO,
                           "OWE: Unexpected Diffie-Hellman group in response: %u",
                           group);
@@ -3889,9 +4536,19 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *resp_ies,
                return -1;
        }
 
+       if (group == 19)
+               prime_len = 32;
+       else if (group == 20)
+               prime_len = 48;
+       else if (group == 21)
+               prime_len = 66;
+       else
+               return -1;
+
        secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0,
                                         elems.owe_dh + 2,
                                         elems.owe_dh_len - 2);
+       secret = wpabuf_zeropad(secret, prime_len);
        if (!secret) {
                wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
                return -1;
@@ -3911,8 +4568,21 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *resp_ies,
        len[0] = wpabuf_len(pub);
        addr[1] = elems.owe_dh + 2;
        len[1] = elems.owe_dh_len - 2;
-       res = sha256_vector(2, addr, len, pmkid);
-       if (res < 0) {
+       if (group == 19) {
+               res = sha256_vector(2, addr, len, pmkid);
+               hash_len = SHA256_MAC_LEN;
+       } else if (group == 20) {
+               res = sha384_vector(2, addr, len, pmkid);
+               hash_len = SHA384_MAC_LEN;
+       } else if (group == 21) {
+               res = sha512_vector(2, addr, len, pmkid);
+               hash_len = SHA512_MAC_LEN;
+       } else {
+               res = -1;
+               hash_len = 0;
+       }
+       pub = wpabuf_zeropad(pub, prime_len);
+       if (res < 0 || !pub) {
                wpabuf_free(pub);
                wpabuf_clear_free(secret);
                return -1;
@@ -3928,29 +4598,70 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *resp_ies,
        wpabuf_put_buf(hkey, pub); /* C */
        wpabuf_free(pub);
        wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */
-       wpabuf_put_le16(hkey, OWE_DH_GROUP); /* group */
-       res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
-                         wpabuf_head(secret), wpabuf_len(secret), prk);
+       wpabuf_put_le16(hkey, sm->owe_group); /* group */
+       if (group == 19)
+               res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
+                                 wpabuf_head(secret), wpabuf_len(secret), prk);
+       else if (group == 20)
+               res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
+                                 wpabuf_head(secret), wpabuf_len(secret), prk);
+       else if (group == 21)
+               res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
+                                 wpabuf_head(secret), wpabuf_len(secret), prk);
        wpabuf_clear_free(hkey);
        wpabuf_clear_free(secret);
        if (res < 0)
                return -1;
 
-       wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, SHA256_MAC_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
 
        /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
 
-       res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL, (const u8 *) info,
-                             os_strlen(info), sm->pmk, PMK_LEN);
-       os_memset(prk, 0, SHA256_MAC_LEN);
-       if (res < 0)
+       if (group == 19)
+               res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
+                                     os_strlen(info), sm->pmk, hash_len);
+       else if (group == 20)
+               res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
+                                     os_strlen(info), sm->pmk, hash_len);
+       else if (group == 21)
+               res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
+                                     os_strlen(info), sm->pmk, hash_len);
+       os_memset(prk, 0, SHA512_MAC_LEN);
+       if (res < 0) {
+               sm->pmk_len = 0;
                return -1;
+       }
+       sm->pmk_len = hash_len;
 
-       wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, PMK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len);
        wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
-       /* TODO: Add PMKSA cache entry */
+       pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0,
+                       bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt,
+                       NULL);
 
        return 0;
 }
 
 #endif /* CONFIG_OWE */
+
+
+void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id)
+{
+#ifdef CONFIG_FILS
+       if (sm && fils_cache_id) {
+               sm->fils_cache_id_set = 1;
+               os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN);
+       }
+#endif /* CONFIG_FILS */
+}
+
+
+#ifdef CONFIG_DPP2
+void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z)
+{
+       if (sm) {
+               wpabuf_clear_free(sm->dpp_z);
+               sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+       }
+}
+#endif /* CONFIG_DPP2 */