[v5,10/16] wpa_supplicant: AP Extended Key ID support
diff mbox series

Message ID 20190825163521.22625-11-alexander@wetzel-home.de
State Superseded
Headers show
Series
  • Support seamless PTK rekeys with Extended Key ID
Related show

Commit Message

Alexander Wetzel Aug. 25, 2019, 4:35 p.m. UTC
Add support for Extended Key ID in hostapd based on IEEE 802.11 - 2016
for infrastructure (AP) connections.

This also fixes an off by one error for key tracking and deletion:
Even within IEEE 802.11w the highest keyid is 5 and not 6.

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
---

This complements the hostapd Extended Key ID patch and adds support
for it also to wpa_supplicant.

For now we only have the "client" Extended Key ID support covered in
wpa_supplicant. IBSS/TDLS/mesh extensions are also missing for now but
besides that it's feature complete. (FILS support is in a separate
patch)

 src/rsn_supp/wpa.c                 | 126 ++++++++++++++++++++++++++---
 src/rsn_supp/wpa.h                 |   5 +-
 src/rsn_supp/wpa_ft.c              |   7 +-
 src/rsn_supp/wpa_i.h               |   3 +
 src/rsn_supp/wpa_ie.c              |  11 +++
 src/rsn_supp/wpa_ie.h              |   1 +
 wpa_supplicant/config.c            |   2 +
 wpa_supplicant/config_file.c       |   1 +
 wpa_supplicant/config_ssid.h       |  10 +++
 wpa_supplicant/ctrl_iface.c        |   3 +
 wpa_supplicant/driver_i.h          |  13 ++-
 wpa_supplicant/wpa_cli.c           |   3 +-
 wpa_supplicant/wpa_supplicant.c    |  27 ++++++-
 wpa_supplicant/wpa_supplicant.conf |   5 ++
 wpa_supplicant/wpas_glue.c         |   4 +-
 15 files changed, 200 insertions(+), 21 deletions(-)

Comments

Alexander Wetzel Sept. 10, 2019, 5:23 p.m. UTC | #1
Am 2019-08-25 um 18:35 schrieb Alexander Wetzel:
> This also fixes an off by one error for key tracking and deletion:
> 
> Even within IEEE 802.11w the highest keyid is 5 and not 6.
> 

No, that's plain wrong and the promised fix is a bug.
The loop processing the variable max reads:
         for (i = 0; i < max; i++) {

Therefore max=6 is correct when we support iGTKs and max=4 is ok when not.

So this part of the patch has now been reverted in my local git:
> --- a/wpa_supplicant/wpa_supplicant.c
> +++ b/wpa_supplicant/wpa_supplicant.c
> @@ -699,7 +699,7 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
>   	int i, max;
>   
>   #ifdef CONFIG_IEEE80211W
> -	max = 6;
> +	max = 5;
>   #else /* CONFIG_IEEE80211W */
>   	max = 4;
>   #endif /* CONFIG_IEEE80211W */

I've also made some other minor changes but these were all cosmetic... 
If there is no immediate interest I would again wait a few days (maybe a 
week) prior sending the updated patch series out again for more issues 
to address.

Alexander

Patch
diff mbox series

diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 830c74c8f..de463cba6 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -581,6 +581,53 @@  static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
 			      sm->pairwise_cipher, z, z_len);
 }
 
+static int handle_extended_key_id(struct wpa_sm *sm,
+				  struct wpa_eapol_ie_parse *kde,
+				  const u8 *rsn_ie, size_t rsn_ie_len)
+{
+	struct wpa_ie_data rsn;
+
+	/* IEEE 802.11 - 2016 requires the Extended Key ID
+	 * bit to be set in the RSN capabilities for both STAs
+	 * to enable the feature
+	 */
+	if (sm->wpa_extended_key_id && rsn_ie &&
+	    sm->pairwise_cipher != WPA_CIPHER_TKIP &&
+	    wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &rsn) >= 0 &&
+	    rsn.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) {
+		if (!kde->key_id) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: No KeyID in Extended Key ID handshake");
+			return -1;
+		} else if (kde->key_id[0] & 0xfe) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: Invalid KeyID");
+			return -1;
+		}
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Using Extended Key ID");
+		sm->keyidx_active = kde->key_id[0];
+		sm->use_extended_key_id = 1;
+	} else {
+		if (kde->key_id && kde->key_id[0]) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: Non-zero KeyID in legacy handshake");
+			return -1;
+		} else if (kde->key_id) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+				"WPA: KeyID in legacy handshake");
+		} else if (!rsn_ie || sm->pairwise_cipher == WPA_CIPHER_TKIP) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: Extended Key ID requires wpa2 and CCMP/GCMP");
+		}
+		if (sm->wpa_extended_key_id)
+			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+				"WPA: Not using Extended Key ID");
+		sm->keyidx_active = 0;
+		sm->use_extended_key_id = 0;
+	}
+	return 0;
+}
 
 static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 					  const unsigned char *src_addr,
@@ -779,6 +826,14 @@  static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
 	wpa_sm_key_request(sm, 0, 1);
 }
 
+static void wpa_supplicant_ptk_installed(struct wpa_sm *sm)
+{
+	if (sm->wpa_ptk_rekey) {
+		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+		eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
+				       sm, NULL);
+	}
+}
 
 static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 				      const struct wpa_eapol_key *key,
@@ -826,12 +881,14 @@  static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 		wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
 	}
 
-	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
-			   sm->ptk.tk, keylen, key_type) < 0) {
+	if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc,
+			   rsclen, sm->ptk.tk, keylen, key_type) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-			"WPA: Failed to set PTK to the "
-			"driver (alg=%d keylen=%d bssid=" MACSTR ")",
-			alg, keylen, MAC2STR(sm->bssid));
+			"WPA: Failed to set PTK to the driver"
+			"(alg=%d keylen=%d bssid=" MACSTR
+			" idx=%d use_extended_key_id=%d key_type=%d)",
+			alg, keylen, MAC2STR(sm->bssid),
+			sm->keyidx_active, sm->use_extended_key_id, key_type);
 		return -1;
 	}
 
@@ -840,12 +897,27 @@  static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 	sm->ptk.tk_len = 0;
 	sm->ptk.installed = 1;
 
-	if (sm->wpa_ptk_rekey) {
-		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
-		eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
-				       sm, NULL);
+	if (key_type != KEY_TYPE_NO_AUTO_TX)
+		wpa_supplicant_ptk_installed(sm);
+
+	return 0;
+}
+
+static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
+{
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: Activate PTK (idx=%d bssid=" MACSTR ")",
+		sm->keyidx_active, MAC2STR(sm->bssid));
+
+	if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active,
+	    0, 0, 0, NULL, 0, KEY_TYPE_SET_TX) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+			"WPA: Failed to activate PTK for Tx (idx=%d bssid="
+			MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid));
+		return -1;
 	}
 
+	wpa_supplicant_ptk_installed(sm);
 	return 0;
 }
 
@@ -1479,6 +1551,9 @@  static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 	if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
 		goto failed;
 
+	if (handle_extended_key_id(sm, &ie, ie.rsn_ie, ie.rsn_ie_len))
+		goto failed;
+
 	if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: ANonce from message 1 of 4-Way Handshake "
@@ -1523,6 +1598,10 @@  static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 		}
 	}
 #endif /* CONFIG_OCV */
+	if (sm->use_extended_key_id) {
+		if (wpa_supplicant_install_ptk(sm, key, KEY_TYPE_NO_AUTO_TX))
+			goto failed;
+	}
 
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
 				       &sm->ptk) < 0) {
@@ -1535,8 +1614,13 @@  static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 	sm->renew_snonce = 1;
 
 	if (key_info & WPA_KEY_INFO_INSTALL) {
-		if (wpa_supplicant_install_ptk(sm, key, KEY_TYPE_PAIRWISE))
+		if (sm->use_extended_key_id) {
+			if (wpa_supplicant_activate_ptk(sm))
+				goto failed;
+		} else if (wpa_supplicant_install_ptk(sm, key,
+						      KEY_TYPE_PAIRWISE)) {
 			goto failed;
+		}
 	}
 
 	if (key_info & WPA_KEY_INFO_SECURE) {
@@ -2650,6 +2734,7 @@  struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
 		return NULL;
 	dl_list_init(&sm->pmksa_candidates);
 	sm->renew_snonce = 1;
+	sm->keyidx_active = 0;
 	sm->ctx = ctx;
 
 	sm->dot11RSNAConfigPMKLifetime = 43200;
@@ -3040,6 +3125,9 @@  int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 	case WPA_PARAM_PAIRWISE:
 		sm->pairwise_cipher = value;
 		break;
+	case WPA_PARAM_EXTENDED_KEY_ID:
+		sm->wpa_extended_key_id = value;
+		break;
 	case WPA_PARAM_GROUP:
 		sm->group_cipher = value;
 		break;
@@ -3164,6 +3252,9 @@  int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 
 #ifdef CONFIG_TESTING_OPTIONS
 	if (sm->test_assoc_ie) {
+		struct wpa_eapol_ie_parse ie;
+		struct wpa_ie_data rsn;
+
 		wpa_printf(MSG_DEBUG,
 			   "TESTING: Replace association WPA/RSN IE");
 		if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie))
@@ -3171,6 +3262,15 @@  int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 		os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie),
 			  wpabuf_len(sm->test_assoc_ie));
 		res = wpabuf_len(sm->test_assoc_ie);
+
+		if (wpa_supplicant_parse_ies(wpa_ie, res, &ie) ||
+		    wpa_parse_wpa_ie_rsn(ie.rsn_ie, ie.rsn_ie_len, &rsn) ||
+		    !(rsn.capabilities &
+		      WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
+			wpa_printf(MSG_DEBUG,
+				   "TESTING: Force disable Extended Key ID");
+			sm->wpa_extended_key_id = 0;
+		}
 	} else
 #endif /* CONFIG_TESTING_OPTIONS */
 	res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
@@ -3400,6 +3500,10 @@  int wpa_sm_has_ptk(struct wpa_sm *sm)
 	return sm->ptk_set;
 }
 
+int wpa_sm_extended_key_id(struct wpa_sm *sm)
+{
+	return sm->wpa_extended_key_id;
+}
 
 void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
 {
@@ -4028,6 +4132,8 @@  static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
 #endif /* CONFIG_IEEE80211W */
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
+	if (sm->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	wpabuf_put_le16(buf, capab);
 
 	/* PMKID Count */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 698154b94..65ca6262e 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -98,7 +98,8 @@  enum wpa_sm_conf_params {
 	WPA_PARAM_MGMT_GROUP,
 	WPA_PARAM_RSN_ENABLED,
 	WPA_PARAM_MFP,
-	WPA_PARAM_OCV
+	WPA_PARAM_OCV,
+	WPA_PARAM_EXTENDED_KEY_ID
 };
 
 struct rsn_supp_config {
@@ -109,6 +110,7 @@  struct rsn_supp_config {
 	void *eap_conf_ctx;
 	const u8 *ssid;
 	size_t ssid_len;
+	int wpa_extended_key_id;
 	int wpa_ptk_rekey;
 	int p2p;
 	int wpa_rsc_relaxation;
@@ -167,6 +169,7 @@  int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
 			const void *network_ctx);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 int wpa_sm_has_ptk(struct wpa_sm *sm);
+int wpa_sm_extended_key_id(struct wpa_sm *sm);
 
 void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
 
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3b45f669b..4aa862748 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -254,6 +254,8 @@  static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 #endif /* CONFIG_IEEE80211W */
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
+	if (sm->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
@@ -411,8 +413,9 @@  static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 
-	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
-			   (u8 *) sm->ptk.tk, keylen, KEY_TYPE_PAIRWISE) < 0) {
+	if (wpa_sm_set_key(sm, alg, bssid, sm->keyidx_active, 1, null_rsc,
+			   sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen,
+			   KEY_TYPE_PAIRWISE) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
 		return -1;
 	}
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 7423e8aee..eee9570ba 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -26,6 +26,7 @@  struct wpa_sm {
 	u8 snonce[WPA_NONCE_LEN];
 	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
 	int renew_snonce;
+	int keyidx_active;
 	u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int rx_replay_counter_set;
 	u8 request_counter[WPA_REPLAY_COUNTER_LEN];
@@ -65,6 +66,8 @@  struct wpa_sm {
 	int wpa_ptk_rekey;
 	int p2p;
 	int wpa_rsc_relaxation;
+	int wpa_extended_key_id;
+	int use_extended_key_id;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index ae9f4ca24..4016ec71a 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -225,6 +225,9 @@  static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
 #endif /* CONFIG_IEEE80211W */
 	if (sm->ocv)
 		capab |= WPA_CAPABILITY_OCVC;
+	if (sm->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
+
 	WPA_PUT_LE16(pos, capab);
 	pos += 2;
 
@@ -417,6 +420,14 @@  static int wpa_parse_generic(const u8 *pos, const u8 *end,
 		return 0;
 	}
 
+	if (pos[1] > RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_KEYID) {
+		ie->key_id = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: KeyID in EAPOL-Key",
+			    pos, pos[1] + 2);
+		return 0;
+	}
+
 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
 		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 9d53973a9..f961e90e4 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -17,6 +17,7 @@  struct wpa_eapol_ie_parse {
 	const u8 *rsn_ie;
 	size_t rsn_ie_len;
 	const u8 *pmkid;
+	const u8 *key_id;
 	const u8 *gtk;
 	size_t gtk_len;
 	const u8 *mac_addr;
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index fc1ed4f90..19a213398 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2334,6 +2334,7 @@  static const struct parse_data ssid_fields[] = {
 	{ INT(dot11MeshConfirmTimeout) },
 	{ INT(dot11MeshHoldingTimeout) },
 #endif /* CONFIG_MESH */
+	{ INT(wpa_extended_key_id) },
 	{ INT(wpa_ptk_rekey) },
 	{ INT(group_rekey) },
 	{ STR(bgscan) },
@@ -2854,6 +2855,7 @@  void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 {
 	ssid->proto = DEFAULT_PROTO;
 	ssid->pairwise_cipher = DEFAULT_PAIRWISE;
+	ssid->wpa_extended_key_id = DEFAULT_EXTENDED_KEY_ID;
 	ssid->group_cipher = DEFAULT_GROUP;
 	ssid->key_mgmt = DEFAULT_KEY_MGMT;
 	ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 91d5caa3f..dadc503eb 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -882,6 +882,7 @@  static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 	INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
 	INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD);
 #endif /* CONFIG_MESH */
+	INT(wpa_extended_key_id);
 	INT(wpa_ptk_rekey);
 	INT(group_rekey);
 	INT(ignore_broadcast_ssid);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index d5c5c00a9..968b1c040 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -22,6 +22,7 @@ 
 #define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
 #define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
 #define DEFAULT_FRAGMENT_SIZE 1398
+#define DEFAULT_EXTENDED_KEY_ID 1
 
 #define DEFAULT_BG_SCAN_PERIOD -1
 #define DEFAULT_MESH_MAX_RETRIES 2
@@ -528,6 +529,15 @@  struct wpa_ssid {
 	unsigned int vht_center_freq1;
 	unsigned int vht_center_freq2;
 
+	/** wpa_extended_key_id - Extended Key ID support
+	 *
+	 * IEEE 802.11-2016 optionally allows to use key id 0 and 1 for PTK keys
+	 * default: auto (1)
+	 * 0 = force off. Do not announce or use Extended Key ID.
+	 * 1 = auto. Use Extended Key ID when possible.
+	 */
+	int wpa_extended_key_id;
+
 	/**
 	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
 	 *
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 862f72734..e5f7246d1 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -5254,6 +5254,9 @@  static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
 			0, KEY_TYPE_PAIRWISE);
+	if (wpa_sm_extended_key_id(wpa_s->wpa))
+		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 1, 0, NULL,
+				0, NULL, 0, KEY_TYPE_PAIRWISE);
 	/* MLME-SETPROTECTION.request(None) */
 	wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
 				   MLME_SETPROTECTION_PROTECT_TYPE_NONE,
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index efb17c471..a63566b7f 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -156,10 +156,19 @@  static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
 				  enum key_type key_type)
 {
 	if (alg != WPA_ALG_NONE) {
-		if (key_idx >= 0 && key_idx <= 6)
+		if (key_idx == 1 &&
+		    (key_type == KEY_TYPE_PAIRWISE ||
+		     key_type == KEY_TYPE_NO_AUTO_TX)) {
+			/* keyidx = 1 can be either a broadcast or - with
+			 * Extended Key ID - an unicast key. Use bit 6 for
+			 * the pairwise keyidx 1.
+			 */
+			wpa_s->keys_cleared &= ~BIT(6);
+		} else if (key_idx >= 0 && key_idx <= 5) {
 			wpa_s->keys_cleared &= ~BIT(key_idx);
-		else
+		} else {
 			wpa_s->keys_cleared = 0;
+		}
 	}
 	if (wpa_s->driver->set_key) {
 		return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 43ac42720..1bad63e08 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1442,7 +1442,8 @@  static const char *network_fields[] = {
 	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
 	"dot11MeshHoldingTimeout",
 #endif /* CONFIG_MESH */
-	"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
+	"wpa_extended_key_id", "wpa_ptk_rekey", "bgscan",
+	"ignore_broadcast_ssid",
 #ifdef CONFIG_P2P
 	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
 #endif /* CONFIG_P2P */
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 56bba65e8..d2be5949c 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -699,7 +699,7 @@  void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 	int i, max;
 
 #ifdef CONFIG_IEEE80211W
-	max = 6;
+	max = 5;
 #else /* CONFIG_IEEE80211W */
 	max = 4;
 #endif /* CONFIG_IEEE80211W */
@@ -711,10 +711,15 @@  void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
 				NULL, 0, KEY_TYPE_BROADCAST);
 	}
-	if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+	/* Pairwise key idx 1 for Extended Key ID is tracked with bit 6 */
+	if (~wpa_s->keys_cleared & (BIT(0) | BIT(6)) && addr &&
 	    !is_zero_ether_addr(addr)) {
-		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
-				0, KEY_TYPE_PAIRWISE);
+		if (!(wpa_s->keys_cleared & (BIT(0))))
+			wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL,
+					0, NULL, 0, KEY_TYPE_PAIRWISE);
+		if (!(wpa_s->keys_cleared & (BIT(6))))
+			wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL,
+					0, NULL, 0, KEY_TYPE_PAIRWISE);
 		/* MLME-SETPROTECTION.request(None) */
 		wpa_drv_mlme_setprotection(
 			wpa_s, addr,
@@ -1237,6 +1242,20 @@  int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 	int sel, proto;
 	const u8 *bss_wpa, *bss_rsn, *bss_osen;
 
+	if (ssid->mode == WPAS_MODE_INFRA && ssid->wpa_extended_key_id &&
+	    wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Enable Extended Key ID support");
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXTENDED_KEY_ID, 1);
+	} else {
+		if (ssid->wpa_extended_key_id)
+			wpa_msg(wpa_s, MSG_INFO,
+				"Extended Key ID not supported");
+		else
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Extended Key ID support disabled");
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXTENDED_KEY_ID, 0);
+	}
+
 	if (bss) {
 		bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 		bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 1159bdcdc..b6a1ed2fd 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1070,6 +1070,11 @@  fast_reauth=1
 # hex without quotation, e.g., 0102030405)
 # wep_tx_keyidx: Default WEP key index (TX) (0..3)
 #
+# wpa_extended_key_id:
+# Support the unicast rekey protocol "Extended Key ID" from IEEE 802.11 - 2016.
+# 0 = force off: Do not announce or use Extended Key ID
+# 1 = auto: Use Extended Key ID when possible (default)
+#
 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
 # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
 #
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index f96608d72..4b0665826 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -505,7 +505,8 @@  static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
 	}
 #endif /* CONFIG_TESTING_GET_GTK */
 #ifdef CONFIG_TESTING_OPTIONS
-	if (addr && !is_broadcast_ether_addr(addr)) {
+	if (addr && !is_broadcast_ether_addr(addr) &&
+	    key_type != KEY_TYPE_SET_TX) {
 		wpa_s->last_tk_alg = alg;
 		os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN);
 		wpa_s->last_tk_key_idx = key_idx;
@@ -1272,6 +1273,7 @@  void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
 #endif /* IEEE8021X_EAPOL */
 		conf.ssid = ssid->ssid;
 		conf.ssid_len = ssid->ssid_len;
+		conf.wpa_extended_key_id = ssid->wpa_extended_key_id;
 		conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
 #ifdef CONFIG_P2P
 		if (ssid->p2p_group && wpa_s->current_bss &&