diff mbox series

[v8,10/15] wpa_supplicant: Extended Key ID support

Message ID 20191031091901.2889-11-alexander@wetzel-home.de
State Changes Requested
Headers show
Series Support seamless PTK rekeys with Extended Key ID | expand

Commit Message

Alexander Wetzel Oct. 31, 2019, 9:18 a.m. UTC
Support Extended Key ID in wpa_supplicant according to
IEEE 802.11 - 2016 for infrastructure (AP) associations.

Extended Key ID allows to rekey pairwise keys without the otherwise
unavoidable MPDU losses on a busy link. The standard is fully backward
compatible, allowing a STA to connect to AP's with and without Extended
Key ID support.

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

This complements the AP Extended Key ID patch for wpa_supplicant.

For now we only have "client" Extended Key ID support covered in
wpa_supplicant. IBSS and potential TDLS/mesh extensions are missing for
now. (A proposal for FILS support is in a separate patch.)

 src/rsn_supp/wpa.c                 | 126 ++++++++++++++++++++++++++---
 src/rsn_supp/wpa.h                 |   3 +
 src/rsn_supp/wpa_ft.c              |   7 +-
 src/rsn_supp/wpa_i.h               |   3 +
 src/rsn_supp/wpa_ie.c              |   3 +
 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           |   4 +-
 wpa_supplicant/wpa_supplicant.c    |  28 ++++++-
 wpa_supplicant/wpa_supplicant.conf |   5 ++
 wpa_supplicant/wpas_glue.c         |   4 +-
 14 files changed, 192 insertions(+), 20 deletions(-)

Comments

Alexander Wetzel Oct. 31, 2019, 2:15 p.m. UTC | #1
> diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
> index 179a60563..525368e53 100644
> --- a/src/rsn_supp/wpa_ft.c
> +++ b/src/rsn_supp/wpa_ft.c
> @@ -256,6 +256,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
>   		capab |= WPA_CAPABILITY_MFPR;
>   	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;
>   
> @@ -422,8 +424,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, 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, 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;
>   	}

These chunks logically belong to patch 13.

If you agree I'll hold back V9, waiting for feedback or more serious 
issues. (As long as all patches of the series are applied to resulting 
code is identical.)

Alexander
diff mbox series

Patch

diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 7039556d9..73c547f01 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 use it with CCMP/GCMP
+	 */
+	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: %d", kde->key_id[0]);
+			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, key_rsc, rsclen,
-			   sm->ptk.tk, keylen, key_type) < 0) {
+	if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 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, 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;
 }
 
@@ -1481,6 +1553,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 "
@@ -1525,6 +1600,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) {
@@ -1537,8 +1616,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) {
@@ -2645,6 +2729,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;
@@ -3035,6 +3120,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;
@@ -3160,6 +3248,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))
@@ -3167,6 +3258,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);
@@ -3504,6 +3604,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)
 {
@@ -4130,6 +4234,8 @@  static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
 		capab |= WPA_CAPABILITY_MFPR;
 	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 6913dd39d..5788c2ce6 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -100,6 +100,7 @@  enum wpa_sm_conf_params {
 	WPA_PARAM_MFP,
 	WPA_PARAM_OCV,
 	WPA_PARAM_SAE_PWE,
+	WPA_PARAM_EXTENDED_KEY_ID
 };
 
 struct rsn_supp_config {
@@ -110,6 +111,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;
@@ -172,6 +174,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 179a60563..525368e53 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -256,6 +256,8 @@  static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 		capab |= WPA_CAPABILITY_MFPR;
 	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;
 
@@ -422,8 +424,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, 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, 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 c1d2ea5e4..2ecb60a5e 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];
@@ -63,6 +64,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 03c0d7e85..c93b76bdc 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -221,6 +221,9 @@  static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
 		capab |= WPA_CAPABILITY_MFPR;
 	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;
 
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 33b35056b..2bd7e0653 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -2494,6 +2494,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) },
@@ -3016,6 +3017,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 cf4b7bc6e..c2a3423f4 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -899,6 +899,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 98db1fe1a..62f1df463 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
@@ -545,6 +546,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 1bcb757e2..8c13abfbb 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -5264,6 +5264,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, 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, 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 0ac2361b1..24f2ea3e6 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -155,10 +155,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 47cec0b10..2df73de6d 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1437,8 +1437,8 @@  static const char *network_fields[] = {
 	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
 	"dot11MeshHoldingTimeout",
 #endif /* CONFIG_MESH */
-	"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
-	"enable_edmg", "edmg_channel",
+	"wpa_extended_key_id", "wpa_ptk_rekey", "bgscan",
+	"ignore_broadcast_ssid", "enable_edmg", "edmg_channel",
 #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 09eb27561..a64121772 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -706,10 +706,15 @@  void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 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, 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, 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, NULL,
+					0, NULL, 0, KEY_TYPE_PAIRWISE);
 		/* MLME-SETPROTECTION.request(None) */
 		wpa_drv_mlme_setprotection(
 			wpa_s, addr,
@@ -1241,6 +1246,23 @@  int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 	int sel, proto;
 	const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
 
+	if (ssid->mode == WPAS_MODE_INFRA && ssid->wpa_extended_key_id &&
+	    ssid->proto & WPA_PROTO_RSN &&
+	    ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 |
+				     WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256) &&
+	    wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID) {
+		wpa_msg(wpa_s, MSG_INFO, "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 ba511b9cb..f64eca023 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -1098,6 +1098,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 2487bb55a..0355c7588 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -510,7 +510,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;
@@ -1277,6 +1278,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 &&