diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 1531c59..6d0d4d2 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2559,6 +2559,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->pmk_r1_push = atoi(pos);
 	} else if (os_strcmp(buf, "ft_over_ds") == 0) {
 		bss->ft_over_ds = atoi(pos);
+	} else if (os_strcmp(buf, "ft_psk_generate_local") == 0) {
+		bss->ft_psk_generate_local = atoi(pos);
 #endif /* CONFIG_IEEE80211R */
 #ifndef CONFIG_NO_CTRL_IFACE
 	} else if (os_strcmp(buf, "ctrl_interface") == 0) {
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index f7a3fe8..7926da3 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1322,6 +1322,14 @@ own_ip_addr=127.0.0.1
 # 1 = FT-over-DS enabled (default)
 #ft_over_ds=1
 
+# Whether to generate FT response locally for PSK networks
+# This avoids to use PULL or PUSH RRB with FT-PSK networks as
+# the required information (psk and other session data)
+# is already locally available.
+# 0 = disabled (default)
+# 1 = enabled
+#ft_psk_generate_local=0
+
 ##### Neighbor table ##########################################################
 # Maximum number of entries kept in AP table (either for neigbor table or for
 # detecting Overlapping Legacy BSS Condition). The oldest entry will be
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 64143e5..92266c0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -330,6 +330,7 @@ struct hostapd_bss_config {
 	struct ft_remote_r1kh *r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
+	int ft_psk_generate_local;
 #endif /* CONFIG_IEEE80211R */
 
 	char *ctrl_interface; /* directory for UNIX domain sockets */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 1a2ff92..14c2681 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -184,6 +184,7 @@ struct wpa_auth_config {
 	struct ft_remote_r1kh *r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
+	int ft_psk_generate_local;
 #endif /* CONFIG_IEEE80211R */
 	int disable_gtk;
 	int ap_mlme;
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 9fef8f2..4319112 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -54,6 +54,17 @@ static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
 }
 
 
+static inline const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+					const u8 *addr, const u8 *p2p_dev_addr,
+					const u8 *prev_psk)
+{
+	if (wpa_auth->cb.get_psk == NULL)
+		return NULL;
+	return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+				    prev_psk);
+}
+
+
 static int
 wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
 	       struct wpa_state_machine **sm,
@@ -493,6 +504,7 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
 	const u8 *ssid = sm->wpa_auth->conf.ssid;
 	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
 	int expiresIn = sm->wpa_auth->conf.r0_key_lifetime * 60;
 	struct ft_vlan vlan;
 
@@ -512,16 +524,20 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
 			  r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
-			    sm->pairwise, vlan, expiresIn);
+	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0,
+				    pmk_r0_name, sm->pairwise, vlan,
+				    expiresIn);
 
 	wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
 			  pmk_r1, sm->pmk_r1_name);
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
-	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
-			    sm->pairwise, vlan, expiresIn);
+	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1,
+				    sm->pmk_r1_name, sm->pairwise, vlan,
+				    expiresIn);
 
 	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
 				 sm->wpa_auth->addr, sm->pmk_r1_name,
@@ -923,6 +939,101 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 }
 
 
+/* Derive PMK-R1 from PSK, check all available PSK
+ */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+			     const u8 *req_pmk_r1_name,
+			     u8 *out_pmk_r1, int *out_pairwise,
+			     struct ft_vlan *out_vlan)
+{
+	const u8 *pmk = NULL;
+	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	const u8 *mdid = wpa_auth->conf.mobility_domain;
+	const u8 *r0kh = sm->r0kh_id;
+	size_t r0kh_len = sm->r0kh_id_len;
+	const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+	const u8 *ssid = wpa_auth->conf.ssid;
+	size_t ssid_len = wpa_auth->conf.ssid_len;
+	int pairwise;
+
+	pairwise = sm->pairwise;
+
+	for (;;) {
+		pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+				     pmk);
+		if (pmk == NULL)
+			break;
+
+		wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+				  r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+		wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+				  pmk_r1, pmk_r1_name);
+
+		if (os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0)
+			continue;
+
+		/* we found a PSK that matches the request pmk_r1_name */
+		wpa_printf(MSG_DEBUG, "FT: found PSK to generate PMK_R1 "
+				      "locally");
+		os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+		if (out_pairwise)
+			*out_pairwise = pairwise;
+		if (out_vlan &&
+		    wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
+			wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
+					      MACSTR, MAC2STR(sm->addr));
+			return -1;
+		}
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "FT: did not find PSK to generate PMK_R1 "
+			      "locally");
+	return -1;
+}
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_keymgmt(struct wpa_state_machine *sm,
+			      struct wpa_ft_ies *parse)
+{
+	int key_mgmt, ciphers;
+
+	if (sm->wpa_key_mgmt)
+		return 0;
+
+	key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid WPA key mgmt (0x%x) from "
+			   MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+		return -1;
+	}
+	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+	ciphers = parse->pairwise_cipher;
+	if (parse->wpa_proto == WPA_PROTO_RSN)
+		ciphers = ciphers & sm->wpa_auth->conf.rsn_pairwise;
+	else
+		ciphers = ciphers & sm->wpa_auth->conf.wpa_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
+			   "from " MACSTR,
+			   parse->wpa_proto == WPA_PROTO_RSN ? "RSN" : "WPA",
+			   parse->pairwise_cipher, MAC2STR(sm->addr));
+		return -1;
+	}
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+	return 0;
+}
+
+
 static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 				   const u8 *ies, size_t ies_len,
 				   u8 **resp_ies, size_t *resp_ies_len)
@@ -985,6 +1096,9 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 		return WLAN_STATUS_INVALID_PMKID;
 	}
 
+	if (wpa_ft_set_keymgmt(sm, &parse) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
 	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
 		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
 	wpa_derive_pmk_r1_name(parse.rsn_pmkid,
@@ -993,6 +1107,12 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
 		    pmk_r1_name, WPA_PMK_NAME_LEN);
 
+	if (conf->ft_psk_generate_local &&
+	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
+				       &vlan) < 0)
+			return WLAN_STATUS_INVALID_PMKID;
+	} else
 	if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
 		    &pairwise, &vlan) < 0) {
 		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index f72ac7f..a85559a 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -81,6 +81,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 	wconf->r1kh_list = conf->r1kh_list;
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
+	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_HS20
 	wconf->disable_gtk = conf->disable_dgaf;
diff --git a/src/common/defs.h b/src/common/defs.h
index b3ac4e8..24ab949 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -79,6 +79,11 @@ static inline int wpa_key_mgmt_ft(int akm)
 			 WPA_KEY_MGMT_FT_SAE));
 }
 
+static inline int wpa_key_mgmt_ft_psk(int akm)
+{
+	return !!(akm & WPA_KEY_MGMT_FT_PSK);
+}
+
 static inline int wpa_key_mgmt_sae(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_SAE |
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index d6295b2..a363206 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -376,6 +376,9 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
 			}
 			if (data.num_pmkid == 1 && data.pmkid)
 				parse->rsn_pmkid = data.pmkid;
+			parse->key_mgmt = data.key_mgmt;
+			parse->pairwise_cipher = data.pairwise_cipher;
+			parse->wpa_proto = data.proto;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
 			if (len < sizeof(struct rsn_mdie))
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index af1d0f0..f0aa1d6 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -430,6 +430,9 @@ struct wpa_ft_ies {
 	size_t igtk_len;
 	const u8 *ric;
 	size_t ric_len;
+	int key_mgmt;
+	int pairwise_cipher;
+	int wpa_proto;
 };
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
