diff mbox series

[v9,05/16] Introduce and add key_flag

Message ID 20200104221015.90469-6-alexander@wetzel-home.de
State Changes Requested
Headers show
Series Seamless PTK rekeys | expand

Commit Message

Alexander Wetzel Jan. 4, 2020, 10:10 p.m. UTC
Add the new attribute "key_flag" which will in the following patches
replace the boolean "set_tx" with something also able to handle
"Extended Key ID for Individually Addressed Frames" from
IEEE 802.11-2016.

The following flag are defined:

  KEY_FLAG_MODIFY
    Set when an already installed key must be updated.
    So far the only use-case is changing RX/TX status of installed
    keys. Must not be set when deleting a key.

  KEY_FLAG_DEFAULT
    Set when the key is also a default key. Must not be set when
    deleting a key. (This is the replacement for set_tx.)

  KEY_FLAG_RX
    The key is valid for RX. Must not be set when deleting a key.

  KEY_FLAG_TX
    The key is valid for TX. Must not be set when deleting a key.

  KEY_FLAG_GROUP
    The key is a broadcast or group key.

  KEY_FLAG_PAIRWISE
    The key is a pairwise key.

  KEY_FLAG_PMK
    The key is a Pairwise Master Key (PMK).

Predefined and needed flag combinations so far are:

  KEY_FLAG_GROUP_RX_TX
    WEP key not used as default key (yet).

  KEY_FLAG_GROUP_RX_TX_DEFAULT
    Default WEP or WPA-NONE key.

  KEY_FLAG_GROUP_RX
    GTK key valid for RX only.

  KEY_FLAG_GROUP_TX_DEFAULT
    GTK key valid for TX only, immediately taking over TX.

  KEY_FLAG_PAIRWISE_RX_TX
    Pairwise key immediately becoming the active pairwise key.

  KEY_FLAG_PAIRWISE_RX
    Pairwise key not yet valid for TX. (Only usable with Extended Key ID
    support.)

  KEY_FLAG_PAIRWISE_RX_TX_MODIFY
    Enable TX for a pairwise key installed with KEY_FLAG_PAIRWISE_RX.

  KEY_FLAG_RX_TX
    Not a valid standalone key type and can only used in combination
    with other flags to mark a key for RX/TX.

This patch is not changing any functionality. It just adds the new
key_flag to all hostapd/wpa_supplicant set_key() functions without using
it, yet.

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
---
 hostapd/ctrl_iface.c            | 45 +++++++++++++++++++++++----------
 src/ap/ap_drv_ops.c             |  4 +--
 src/ap/ap_drv_ops.h             |  2 +-
 src/ap/hostapd.c                | 16 +++++++-----
 src/ap/ieee802_11.c             |  5 +++-
 src/ap/ieee802_1x.c             |  8 +++---
 src/ap/wpa_auth.c               | 18 ++++++++-----
 src/ap/wpa_auth.h               |  3 ++-
 src/ap/wpa_auth_ft.c            |  7 ++---
 src/ap/wpa_auth_glue.c          |  4 +--
 src/common/defs.h               | 22 ++++++++++++++++
 src/drivers/driver.h            | 43 ++++++++++++++++++++++++++++++-
 src/drivers/driver_atheros.c    |  3 ++-
 src/drivers/driver_bsd.c        |  3 ++-
 src/drivers/driver_hostap.c     |  3 ++-
 src/drivers/driver_ndis.c       | 12 +++++----
 src/drivers/driver_nl80211.c    | 11 +++++---
 src/drivers/driver_openbsd.c    |  3 ++-
 src/drivers/driver_privsep.c    |  3 ++-
 src/drivers/driver_wext.c       |  8 +++---
 src/drivers/driver_wext.h       |  3 ++-
 src/rsn_supp/tdls.c             |  7 ++---
 src/rsn_supp/wpa.c              | 17 ++++++++-----
 src/rsn_supp/wpa.h              |  2 +-
 src/rsn_supp/wpa_ft.c           | 11 +++++---
 src/rsn_supp/wpa_i.h            |  5 ++--
 tests/hwsim/test_ap_ciphers.py  |  2 +-
 wpa_supplicant/ctrl_iface.c     | 26 ++++++++++++-------
 wpa_supplicant/driver_i.h       |  6 +++--
 wpa_supplicant/ibss_rsn.c       | 11 ++++----
 wpa_supplicant/mesh_mpm.c       |  9 ++++---
 wpa_supplicant/mesh_rsn.c       | 10 +++++---
 wpa_supplicant/wpa_supplicant.c | 12 ++++++---
 wpa_supplicant/wpas_glue.c      | 13 ++++++----
 34 files changed, 249 insertions(+), 108 deletions(-)

Comments

Jouni Malinen Jan. 6, 2020, 9:09 a.m. UTC | #1
On Sat, Jan 04, 2020 at 11:10:04PM +0100, Alexander Wetzel wrote:
> Add the new attribute "key_flag" which will in the following patches
> replace the boolean "set_tx" with something also able to handle
> "Extended Key ID for Individually Addressed Frames" from
> IEEE 802.11-2016.
...

Is this dependent on the CAN_REPLACE_PTK0 changes? If yes, I guess I'll
leave this waiting for my comments on those to be addressed. If not, it
would be good to split this patch set into two or more parts since it is
not exactly convenient to get this many patches reviewed in a single set
unless all the changes are trivial or at least straightforward. Maybe it
would be better to split this into three
(NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, set_key() extensions/cleanup, and
Extended Key ID) and get them in one by one set instead of resending all
16 patches if anything needs to change.
Jouni Malinen Jan. 6, 2020, 9:17 a.m. UTC | #2
On Sat, Jan 04, 2020 at 11:10:04PM +0100, Alexander Wetzel wrote:
> +enum key_flag {
> +	KEY_FLAG_MODIFY			= BIT(0),
> +	KEY_FLAG_DEFAULT		= BIT(1),
> +	KEY_FLAG_RX			= BIT(2),
> +	KEY_FLAG_TX			= BIT(3),
> +	KEY_FLAG_GROUP			= BIT(4),
> +	KEY_FLAG_PAIRWISE		= BIT(5),
> +	KEY_FLAG_PMK			= BIT(6),

How much of this is really needed for new functionality vs. cleaning up?
I'm not necessarily against these flags, but I'll note that the addr
parameter is already documented to indicate broadcast vs. default vs.
unicast keys.

> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
> @@ -2359,7 +2400,7 @@ struct wpa_driver_ops {
>  	int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg,
>  		       const u8 *addr, int key_idx, int set_tx,
>  		       const u8 *seq, size_t seq_len,
> -		       const u8 *key, size_t key_len);
> +		       const u8 *key, size_t key_len, enum key_flag key_flag);

Instead of doing these set_key() prototype changes in driver.h and all
driver_*.c in this patch 5/16 and then repeat that in 9/16, maybe it
would make more sense to have a new patch before 5/16 that converts
set_key() to take in a struct of parameter values similarly to
associate(). In other words, only take in void *priv and struct
wpa_driver_key_params *params. This change would only touch
src/drivers/* and the set_key() wrapper functions in
wpa_supplicant/driver_i.h and src/ap/ap_drv_ops.c. Addition of a new
key_flag into the struct could then be done without having to touch any
driver_*.c and similarly, removal of set_tx would be trivial once there
are no remaining users for it.
Alexander Wetzel Jan. 6, 2020, 12:20 p.m. UTC | #3
Am 06.01.20 um 10:09 schrieb Jouni Malinen:
> On Sat, Jan 04, 2020 at 11:10:04PM +0100, Alexander Wetzel wrote:
>> Add the new attribute "key_flag" which will in the following patches
>> replace the boolean "set_tx" with something also able to handle
>> "Extended Key ID for Individually Addressed Frames" from
>> IEEE 802.11-2016.
> ...
> 
> Is this dependent on the CAN_REPLACE_PTK0 changes? If yes, I guess I'll
> leave this waiting for my comments on those to be addressed. If not, it
> would be good to split this patch set into two or more parts since it is
> not exactly convenient to get this many patches reviewed in a single set
> unless all the changes are trivial or at least straightforward. Maybe it
> would be better to split this into three
> (NL80211_EXT_FEATURE_CAN_REPLACE_PTK0, set_key() extensions/cleanup, and
> Extended Key ID) and get them in one by one set instead of resending all
> 16 patches if anything needs to change.
> 

I was original hoping to get Extended Key ID merged till PTK0 rekey 
became ready, so we would not have to discuss the interactions.
But then the PTK0 rekey modifications needed very little extra effort 
and were ready much faster than assumed.

There are some interactions but nothing major. Nevertheless both PTK0 
rekeying and Extended Key ID patches would a bit different without the 
other and for Extended Key ID the v8 version of the series is actual 
showing what.

The only hard dependency is the new key_flag API for extended Key ID.
Which as a standalone would look like overkill without the new special 
Extended Key ID cases and just be a small fix to set_tx usage.

PTK0 rekeying is setting up a new callback for the eapol SM to find out 
from the WPA SM if it can (re-)install a PTK or should abandon the eapol 
reauthentication immediately. With Extended Key ID only the WPA SM can 
answer that question. So the PTK0 rekey patches are handling that what 
seems to be a subotimal way but is later needed.

I can post that as three different series assuming the previous patches 
have been also applied. This should cut down the resend. (But not V9, 
which had changes in all areas.)

In fact you can already abort the series after the PTK0 patches (I did 
verify that is a valid break point) and while not tested much the same 
should be true for the key_flag API changes.

Is that ok or would you like to have the PTK0 rekey patch as it would be 
without Extended Key ID on the radar and via versa? and would you then 
prefer to postpone the additional key_flags only used with Extended key 
ID to the Extended Key ID series?

Alexander
Alexander Wetzel Jan. 6, 2020, 12:53 p.m. UTC | #4
Am 06.01.20 um 10:17 schrieb Jouni Malinen:
> On Sat, Jan 04, 2020 at 11:10:04PM +0100, Alexander Wetzel wrote:
>> +enum key_flag {
>> +	KEY_FLAG_MODIFY			= BIT(0),
>> +	KEY_FLAG_DEFAULT		= BIT(1),
>> +	KEY_FLAG_RX			= BIT(2),
>> +	KEY_FLAG_TX			= BIT(3),
>> +	KEY_FLAG_GROUP			= BIT(4),
>> +	KEY_FLAG_PAIRWISE		= BIT(5),
>> +	KEY_FLAG_PMK			= BIT(6),
> 
> How much of this is really needed for new functionality vs. cleaning up?
> I'm not necessarily against these flags, but I'll note that the addr
> parameter is already documented to indicate broadcast vs. default vs.
> unicast keys.
> 

I believe they are. And also that the documentation of addr should be 
changed then, too:-)

The MODIFY Flag is not needed till we merge Extended Key ID (nobody is 
changing a TX status so far). The PMK flag could also be called 
something like "OTHER" or we also could just use "0" for them.
Just looking at the addr to figure out if we have a default key is 
missing the (existing) special case of IBSS GTK keys. (Search for "RSN 
IBSS RX GTK" in the driver) and look at supp_set_key() in  ibss_rsn.c 
which has this code block:
         if (is_broadcast_ether_addr(addr))
                 addr = peer->addr;
         return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
                                set_tx, seq, seq_len, key, key_len);

There also is the issue of the dual-usage of keyid 1 with Extended Key 
ID when we delete a key. We have to figure out if the pairwise or the 
group key must be deleted. (But just making sure pairwise keys are 
flagged accordingly would sove that, it just would be a bit less obvious.)

That said we could achieve something similar with a slight update to the 
key_flag API proposed in V8. But I really liked how the API proposed 
here did clean up the special case handling in nl80211 and making that 
much simpler.

>> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
>> @@ -2359,7 +2400,7 @@ struct wpa_driver_ops {
>>   	int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg,
>>   		       const u8 *addr, int key_idx, int set_tx,
>>   		       const u8 *seq, size_t seq_len,
>> -		       const u8 *key, size_t key_len);
>> +		       const u8 *key, size_t key_len, enum key_flag key_flag);
> 
> Instead of doing these set_key() prototype changes in driver.h and all
> driver_*.c in this patch 5/16 and then repeat that in 9/16, maybe it
> would make more sense to have a new patch before 5/16 that converts
> set_key() to take in a struct of parameter values similarly to
> associate(). In other words, only take in void *priv and struct
> wpa_driver_key_params *params. This change would only touch
> src/drivers/* and the set_key() wrapper functions in
> wpa_supplicant/driver_i.h and src/ap/ap_drv_ops.c. Addition of a new
> key_flag into the struct could then be done without having to touch any
> driver_*.c and similarly, removal of set_tx would be trivial once there
> are no remaining users for it.

Ok, V10 of the series (or the three replacement series for it) will make 
that change. (Will probably at least some days till I get to that.)

Alexander
Jouni Malinen Jan. 9, 2020, 11:12 a.m. UTC | #5
On Mon, Jan 06, 2020 at 01:53:45PM +0100, Alexander Wetzel wrote:
> That said we could achieve something similar with a slight update to the
> key_flag API proposed in V8. But I really liked how the API proposed here
> did clean up the special case handling in nl80211 and making that much
> simpler.

OK. I applied this patch 5/16 rebased on top of the other set_key()
parameter addition I mentioned.
Alexander Wetzel Jan. 9, 2020, 5:58 p.m. UTC | #6
Am 09.01.20 um 12:12 schrieb Jouni Malinen:
> On Mon, Jan 06, 2020 at 01:53:45PM +0100, Alexander Wetzel wrote:
>> That said we could achieve something similar with a slight update to the
>> key_flag API proposed in V8. But I really liked how the API proposed here
>> did clean up the special case handling in nl80211 and making that much
>> simpler.
> 
> OK. I applied this patch 5/16 rebased on top of the other set_key()
> parameter addition I mentioned.
> 

Oh no, looks like we had a misunderstanding and I wasted quite some time:
I've also migrated set_key to use a param struct and also rebased all 
the other patches of V9.. I planned to send you the PTK0 rekey and the 
API patches tomorrow.

 From what I see you really only updated the driver API. I've pushed the 
change though all layers and that was 90k big.

Just looking at the file names I'm touching those additional files:

src/ap/ap_drv_ops.h
src/ap/hostapd.c
src/ap/ieee802_11.c
src/ap/ieee802_1x.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_ft.c
src/ap/wpa_auth_glue.c
src/rsn_supp/tdls.c
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
src/rsn_supp/wpa_ft.c
src/rsn_supp/wpa_i.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/ibss_rsn.c
wpa_supplicant/mesh_mpm.c
wpa_supplicant/mesh_rsn.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpas_glue.c

I migrated all set_key() to use a param struct. Are you interested in 
the that?

My current "draft" is still against 09f96acb9 I'm now rebasing so I can 
post it here.

Alexander
Jouni Malinen Jan. 9, 2020, 9:02 p.m. UTC | #7
On Thu, Jan 09, 2020 at 06:58:19PM +0100, Alexander Wetzel wrote:
> From what I see you really only updated the driver API. I've pushed the
> change though all layers and that was 90k big.

My main goal was to isolate the driver_*.c changes from core
hostapd/wpa_supplicant changes so that it would be straightforward to
handle the driver wrapper changes one by one and independent of the
changes in the core implementation.

> I migrated all set_key() to use a param struct. Are you interested in the
> that?

I'm not sure there would be significant value in doing that conversion
to all callers at this point in time. I really want to see the patches
being minimal in size when adding new functionality.. This migration of
param struct throughout all the layers would not sound like something
that would be needed for anything new we are discussing and I'm not
convinced it would result in simpler and more convenient implementation
to maintain in the future. I was mainly thinking of potentially getting
rid of the set_tx argument to hostapd_drv_set_key() and
wpa_drv_set_key() if that value can now be reliably determined from the
key_flag bits. And as a separate change, remove it also from the
EAPOL/RSN state machine callback functions for setting keys.
diff mbox series

Patch

diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index cb4d8fac2..98dcf09a4 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2155,7 +2155,8 @@  static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
 					hapd->last_igtk_alg,
 					broadcast_ether_addr,
 					hapd->last_igtk_key_idx, 1, NULL, 0,
-					zero, hapd->last_igtk_len) < 0)
+					zero, hapd->last_igtk_len,
+					KEY_FLAG_GROUP_TX_DEFAULT) < 0)
 			return -1;
 
 		/* Set the previously configured key to reset its TSC */
@@ -2164,7 +2165,8 @@  static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
 					   broadcast_ether_addr,
 					   hapd->last_igtk_key_idx, 1, NULL, 0,
 					   hapd->last_igtk,
-					   hapd->last_igtk_len);
+					   hapd->last_igtk_len,
+					   KEY_FLAG_GROUP_TX_DEFAULT);
 	}
 
 	if (is_broadcast_ether_addr(addr)) {
@@ -2179,7 +2181,8 @@  static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
 					hapd->last_gtk_alg,
 					broadcast_ether_addr,
 					hapd->last_gtk_key_idx, 1, NULL, 0,
-					zero, hapd->last_gtk_len) < 0)
+					zero, hapd->last_gtk_len,
+					KEY_FLAG_GROUP_TX_DEFAULT) < 0)
 			return -1;
 
 		/* Set the previously configured key to reset its TSC */
@@ -2187,7 +2190,8 @@  static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
 					   hapd->last_gtk_alg,
 					   broadcast_ether_addr,
 					   hapd->last_gtk_key_idx, 1, NULL, 0,
-					   hapd->last_gtk, hapd->last_gtk_len);
+					   hapd->last_gtk, hapd->last_gtk_len,
+					   KEY_FLAG_GROUP_TX_DEFAULT);
 	}
 
 	sta = ap_get_sta(hapd, addr);
@@ -2204,13 +2208,15 @@  static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
 	 * in the driver. */
 	if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
 				sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
-				zero, sta->last_tk_len) < 0)
+				zero, sta->last_tk_len,
+				KEY_FLAG_PAIRWISE_RX_TX) < 0)
 		return -1;
 
 	/* Set the previously configured key to reset its TSC/RSC */
 	return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
 				   sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
-				   sta->last_tk, sta->last_tk_len);
+				   sta->last_tk, sta->last_tk_len,
+				   KEY_FLAG_PAIRWISE_RX_TX);
 }
 
 
@@ -2219,11 +2225,12 @@  static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
 	u8 addr[ETH_ALEN];
 	const char *pos = cmd;
 	enum wpa_alg alg;
+	enum key_flag key_flag;
 	int idx, set_tx;
 	u8 seq[6], key[WPA_TK_MAX_LEN];
 	size_t key_len;
 
-	/* parameters: alg addr idx set_tx seq key */
+	/* parameters: alg addr idx set_tx seq key key_flag */
 
 	alg = atoi(pos);
 	pos = os_strchr(pos, ' ');
@@ -2252,13 +2259,24 @@  static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
 	if (*pos != ' ')
 		return -1;
 	pos++;
-	key_len = os_strlen(pos) / 2;
+	if (!os_strchr(pos, ' '))
+		return -1;
+	key_len = (os_strchr(pos, ' ') - pos) / 2;
 	if (hexstr2bin(pos, key, key_len) < 0)
 		return -1;
+	pos += 2 * key_len;
+	if (*pos != ' ')
+		return -1;
+
+	pos++;
+	key_flag = atoi(pos);
+	pos = os_strchr(pos, ' ');
+	if (pos)
+		return -1;
 
 	wpa_printf(MSG_INFO, "TESTING: Set key");
 	return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx,
-				   set_tx, seq, 6, key, key_len);
+				   set_tx, seq, 6, key, key_len, key_flag);
 }
 
 
@@ -2274,7 +2292,8 @@  static void restore_tk(void *ctx1, void *ctx2)
 	 * preventing encryption of a single EAPOL frame. */
 	hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
 			    sta->addr, sta->last_tk_key_idx, 1, NULL, 0,
-			    sta->last_tk, sta->last_tk_len);
+			    sta->last_tk, sta->last_tk_len,
+			    KEY_FLAG_PAIRWISE_RX_TX);
 }
 
 
@@ -2298,7 +2317,7 @@  static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
 			   MAC2STR(sta->addr));
 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
 				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
-				    NULL, 0);
+				    NULL, 0, KEY_FLAG_PAIRWISE);
 	}
 
 	wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
@@ -2328,7 +2347,7 @@  static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
 			   MAC2STR(sta->addr));
 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
 				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
-				    NULL, 0);
+				    NULL, 0, KEY_FLAG_PAIRWISE);
 	}
 
 	wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
@@ -2358,7 +2377,7 @@  static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
 			   MAC2STR(sta->addr));
 		hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
 				    sta->addr, sta->last_tk_key_idx, 0, NULL, 0,
-				    NULL, 0);
+				    NULL, 0, KEY_FLAG_PAIRWISE);
 	}
 
 	wpa_printf(MSG_INFO,
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 35ecf4102..62f41b758 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -682,13 +682,13 @@  int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
 			enum wpa_alg alg, const u8 *addr,
 			int key_idx, int set_tx,
 			const u8 *seq, size_t seq_len,
-			const u8 *key, size_t key_len)
+			const u8 *key, size_t key_len, enum key_flag key_flag)
 {
 	if (hapd->driver == NULL || hapd->driver->set_key == NULL)
 		return 0;
 	return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
 				     key_idx, set_tx, seq, seq_len, key,
-				     key_len);
+				     key_len, key_flag);
 }
 
 
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index b0170df58..ca31ade86 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -91,7 +91,7 @@  int hostapd_drv_set_key(const char *ifname,
 			enum wpa_alg alg, const u8 *addr,
 			int key_idx, int set_tx,
 			const u8 *seq, size_t seq_len,
-			const u8 *key, size_t key_len);
+			const u8 *key, size_t key_len, enum key_flag key_flag);
 int hostapd_drv_send_mlme(struct hostapd_data *hapd,
 			  const void *msg, size_t len, int noack,
 			  const u16 *csa_offs, size_t csa_offs_len,
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 55dd11e06..0276515d7 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -292,8 +292,8 @@  static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
 	if (!ifname || !hapd->drv_priv)
 		return;
 	for (i = 0; i < NUM_WEP_KEYS; i++) {
-		if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
-					0, NULL, 0, NULL, 0)) {
+		if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, 0,
+					NULL, 0, NULL, 0, KEY_FLAG_GROUP)) {
 			wpa_printf(MSG_DEBUG, "Failed to clear default "
 				   "encryption keys (ifname=%s keyidx=%d)",
 				   ifname, i);
@@ -302,8 +302,8 @@  static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
 	if (hapd->conf->ieee80211w) {
 		for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
 			if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
-						NULL, i, 0, NULL,
-						0, NULL, 0)) {
+						NULL, i, 0, NULL, 0,
+						NULL, 0, KEY_FLAG_GROUP)) {
 				wpa_printf(MSG_DEBUG, "Failed to clear "
 					   "default mgmt encryption keys "
 					   "(ifname=%s keyidx=%d)", ifname, i);
@@ -330,7 +330,8 @@  static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
 	    hostapd_drv_set_key(hapd->conf->iface,
 				hapd, WPA_ALG_WEP, broadcast_ether_addr, idx,
 				1, NULL, 0, ssid->wep.key[idx],
-				ssid->wep.len[idx])) {
+				ssid->wep.len[idx],
+				KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
 		wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
 		errors++;
 	}
@@ -556,7 +557,10 @@  static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
 		    hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
 					i == hapd->conf->ssid.wep.idx, NULL, 0,
 					hapd->conf->ssid.wep.key[i],
-					hapd->conf->ssid.wep.len[i])) {
+					hapd->conf->ssid.wep.len[i],
+					i == hapd->conf->ssid.wep.idx ?
+					     KEY_FLAG_GROUP_RX_TX_DEFAULT :
+					     KEY_FLAG_GROUP_RX_TX)) {
 			wpa_printf(MSG_WARNING, "Could not set WEP "
 				   "encryption.");
 			return -1;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 84dea20ca..bee65ac3b 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4880,7 +4880,10 @@  static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
 		if (ssid->wep.key[i] &&
 		    hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
 					i == ssid->wep.idx, NULL, 0,
-					ssid->wep.key[i], ssid->wep.len[i])) {
+					ssid->wep.key[i], ssid->wep.len[i],
+					i == ssid->wep.idx ?
+					     KEY_FLAG_GROUP_RX_TX_DEFAULT :
+					     KEY_FLAG_GROUP_RX_TX)) {
 			wpa_printf(MSG_WARNING,
 				   "Could not set WEP keys for WDS interface; %s",
 				   ifname_wds);
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d0810310c..9b26ff1d7 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -285,7 +285,8 @@  static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
 		 * has ACKed EAPOL-Key frame */
 		if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
 					sta->addr, 0, 1, NULL, 0, ikey,
-					hapd->conf->individual_wep_key_len)) {
+					hapd->conf->individual_wep_key_len,
+					KEY_FLAG_PAIRWISE_RX_TX)) {
 			wpa_printf(MSG_ERROR,
 				   "Could not set individual WEP encryption");
 		}
@@ -2179,7 +2180,8 @@  static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
 				broadcast_ether_addr,
 				eapol->default_wep_key_idx, 1, NULL, 0,
 				eapol->default_wep_key,
-				hapd->conf->default_wep_key_len)) {
+				hapd->conf->default_wep_key_len,
+				KEY_FLAG_GROUP_RX_TX_DEFAULT)) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_WARNING,
 			       "failed to configure a new broadcast key");
@@ -2471,7 +2473,7 @@  int ieee802_1x_init(struct hostapd_data *hapd)
 		for (i = 0; i < 4; i++)
 			hostapd_drv_set_key(hapd->conf->iface, hapd,
 					    WPA_ALG_NONE, NULL, i, 0, NULL, 0,
-					    NULL, 0);
+					    NULL, 0, KEY_FLAG_GROUP_RX_TX);
 
 		ieee802_1x_rekey(hapd, NULL);
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 5c52f0f76..7298e4928 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -136,12 +136,13 @@  static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
 static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 				   int vlan_id,
 				   enum wpa_alg alg, const u8 *addr, int idx,
-				   u8 *key, size_t key_len)
+				   u8 *key, size_t key_len,
+				   enum key_flag key_flag)
 {
 	if (wpa_auth->cb->set_key == NULL)
 		return -1;
 	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
-				     key, key_len);
+				     key, key_len, key_flag);
 }
 
 
@@ -1723,7 +1724,7 @@  void wpa_remove_ptk(struct wpa_state_machine *sm)
 	sm->PTK_valid = FALSE;
 	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
 	if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
-			     0))
+			     0, KEY_FLAG_PAIRWISE))
 		wpa_printf(MSG_DEBUG,
 			   "RSN: PTK removal from the driver failed");
 	sm->pairwise_set = FALSE;
@@ -2766,7 +2767,7 @@  int fils_set_tk(struct wpa_state_machine *sm)
 
 	wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
 	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-			     sm->PTK.tk, klen)) {
+			     sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX)) {
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
 		return -1;
 	}
@@ -3391,7 +3392,8 @@  SM_STATE(WPA_PTK, PTKINITDONE)
 		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
 		int klen = wpa_cipher_key_len(sm->pairwise);
 		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-				     sm->PTK.tk, klen)) {
+				     sm->PTK.tk, klen,
+				     KEY_FLAG_PAIRWISE_RX_TX)) {
 			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
@@ -3983,7 +3985,8 @@  static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
 			     wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
 			     broadcast_ether_addr, group->GN,
-			     group->GTK[group->GN - 1], group->GTK_len) < 0)
+			     group->GTK[group->GN - 1], group->GTK_len,
+			     KEY_FLAG_GROUP_TX_DEFAULT) < 0)
 		ret = -1;
 
 	if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
@@ -3996,7 +3999,8 @@  static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 		if (ret == 0 &&
 		    wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
 				     broadcast_ether_addr, group->GN_igtk,
-				     group->IGTK[group->GN_igtk - 4], len) < 0)
+				     group->IGTK[group->GN_igtk - 4],
+				     len, KEY_FLAG_GROUP_TX_DEFAULT) < 0)
 			ret = -1;
 	}
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 0e44cbe0b..64c261964 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -260,7 +260,8 @@  struct wpa_auth_callbacks {
 			      int *vlan_id);
 	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
 	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
-		       const u8 *addr, int idx, u8 *key, size_t key_len);
+		       const u8 *addr, int idx, u8 *key, size_t key_len,
+		       enum key_flag key_flag);
 	int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
 	int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
 			  size_t data_len, int encrypt);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index d24feb560..462876195 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2622,12 +2622,13 @@  u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 				   int vlan_id,
 				   enum wpa_alg alg, const u8 *addr, int idx,
-				   u8 *key, size_t key_len)
+				   u8 *key, size_t key_len,
+				   enum key_flag key_flag)
 {
 	if (wpa_auth->cb->set_key == NULL)
 		return -1;
 	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
-				     key, key_len);
+				     key, key_len, key_flag);
 }
 
 
@@ -2660,7 +2661,7 @@  void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 	 * optimized by adding the STA entry earlier.
 	 */
 	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-			     sm->PTK.tk, klen))
+			     sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
 		return;
 
 	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 951c7364b..84e21fe11 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -362,7 +362,7 @@  static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
 
 static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 				    const u8 *addr, int idx, u8 *key,
-				    size_t key_len)
+				    size_t key_len, enum key_flag key_flag)
 {
 	struct hostapd_data *hapd = ctx;
 	const char *ifname = hapd->conf->iface;
@@ -403,7 +403,7 @@  static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
-				   key, key_len);
+				   key, key_len, key_flag);
 }
 
 
diff --git a/src/common/defs.h b/src/common/defs.h
index 1cf027340..a4e002f5e 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -429,4 +429,26 @@  enum ptk0_rekey_handling {
 	PTK0_REKEY_ALLOW_NEVER
 };
 
+enum key_flag {
+	KEY_FLAG_MODIFY			= BIT(0),
+	KEY_FLAG_DEFAULT		= BIT(1),
+	KEY_FLAG_RX			= BIT(2),
+	KEY_FLAG_TX			= BIT(3),
+	KEY_FLAG_GROUP			= BIT(4),
+	KEY_FLAG_PAIRWISE		= BIT(5),
+	KEY_FLAG_PMK			= BIT(6),
+	/* Used flag combinations */
+	KEY_FLAG_RX_TX			= KEY_FLAG_RX | KEY_FLAG_TX,
+	KEY_FLAG_GROUP_RX_TX		= KEY_FLAG_GROUP | KEY_FLAG_RX_TX,
+	KEY_FLAG_GROUP_RX_TX_DEFAULT	= KEY_FLAG_GROUP_RX_TX |
+					  KEY_FLAG_DEFAULT,
+	KEY_FLAG_GROUP_RX		= KEY_FLAG_GROUP | KEY_FLAG_RX,
+	KEY_FLAG_GROUP_TX_DEFAULT	= KEY_FLAG_GROUP | KEY_FLAG_TX |
+					  KEY_FLAG_DEFAULT,
+	KEY_FLAG_PAIRWISE_RX_TX		= KEY_FLAG_PAIRWISE | KEY_FLAG_RX_TX,
+	KEY_FLAG_PAIRWISE_RX		= KEY_FLAG_PAIRWISE | KEY_FLAG_RX,
+	KEY_FLAG_PAIRWISE_RX_TX_MODIFY	= KEY_FLAG_PAIRWISE_RX_TX |
+					  KEY_FLAG_MODIFY,
+};
+
 #endif /* DEFS_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 6288fe103..a89122c37 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2335,6 +2335,47 @@  struct wpa_driver_ops {
 	 *	8-byte Rx Mic Key
 	 * @key_len: length of the key buffer in octets (WEP: 5 or 13,
 	 *	TKIP: 32, CCMP/GCMP: 16, IGTK: 16)
+	 * @key_flag: Additional key flags:
+	 *	%KEY_FLAG_MODIFY
+	 *	  Set when an already installed key must be updated.
+	 *	  So far the only use-case is changing RX/TX status of
+	 *	  installed keys. Must not be set when deleting a key.
+	 *	%KEY_FLAG_DEFAULT
+	 *	  Set when the key is also a default key. Must not be set when
+	 *	  deleting a key.
+	 *	%KEY_FLAG_RX
+	 *	  The key is valid for RX. Must not be set when deleting a key.
+	 *	%KEY_FLAG_TX
+	 *	  The key is valid for TX. Must not be set when deleting a key.
+	 *	%KEY_FLAG_GROUP
+	 *	  The key is a broadcast or group key.
+	 *	%KEY_FLAG_PAIRWISE
+	 *	  The key is a pairwise key.
+	 *	%KEY_FLAG_PMK
+	 *	  The key is a Pairwise Master Key (PMK).
+	 *
+	 *	Valid and pre-defined combinations are:
+	 *	%KEY_FLAG_GROUP_RX_TX
+	 *	  WEP key not to be installed as default key.
+	 *	%KEY_FLAG_GROUP_RX_TX_DEFAULT
+	 *	  Default WEP or WPA-NONE key.
+	 *	%KEY_FLAG_GROUP_RX
+	 *	  GTK key valid for RX only.
+	 *	%KEY_FLAG_GROUP_TX_DEFAULT
+	 *	  GTK key valid for TX only, immediately taking over TX.
+	 *	%KEY_FLAG_PAIRWISE_RX_TX
+	 *	  Pairwise key immediately becoming the active pairwise key.
+	 *	%KEY_FLAG_PAIRWISE_RX
+	 *	  Pairwise key not yet valid for TX. (Only usable when Extended
+	 *	  Key ID is supported by the driver.)
+	 *	%KEY_FLAG_PAIRWISE_RX_TX_MODIFY
+	 *	  Enable TX for a pairwise key installed with
+	 *	  KEY_FLAG_PAIRWISE_RX.
+	 *
+	 *	Not a valid standalone key type but pre-defined to be combined
+	 *	with other key_flags:
+	 *	%KEY_FLAG_RX_TX
+	 *	  RX/TX key.
 	 *
 	 * Returns: 0 on success, -1 on failure
 	 *
@@ -2359,7 +2400,7 @@  struct wpa_driver_ops {
 	int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg,
 		       const u8 *addr, int key_idx, int set_tx,
 		       const u8 *seq, size_t seq_len,
-		       const u8 *key, size_t key_len);
+		       const u8 *key, size_t key_len, enum key_flag key_flag);
 
 	/**
 	 * init - Initialize driver interface
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index de25e4e4e..4d9e9011c 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -494,7 +494,8 @@  atheros_del_key(void *priv, const u8 *addr, int key_idx)
 static int
 atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 		const u8 *addr, int key_idx, int set_tx, const u8 *seq,
-		size_t seq_len, const u8 *key, size_t key_len)
+		size_t seq_len, const u8 *key, size_t key_len,
+		enum key_flag key_flag)
 {
 	struct atheros_driver_data *drv = priv;
 	struct ieee80211req_key wk;
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 8667ee519..5ee6eb730 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -333,7 +333,8 @@  bsd_ctrl_iface(void *priv, int enable)
 static int
 bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 	    const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
-	    size_t seq_len, const u8 *key, size_t key_len)
+	    size_t seq_len, const u8 *key, size_t key_len,
+	    enum key_flag key_flag)
 {
 	struct ieee80211req_key wk;
 #ifdef IEEE80211_KEY_NOREPLAY
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index dbe7fafc8..818188ada 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -400,7 +400,8 @@  static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
 				     enum wpa_alg alg, const u8 *addr,
 				     int key_idx, int set_tx,
 				     const u8 *seq, size_t seq_len,
-				     const u8 *key, size_t key_len)
+				     const u8 *key, size_t key_len,
+				     enum key_flag key_flag)
 {
 	struct hostap_driver_data *drv = priv;
 	struct prism2_hostapd_param *param;
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 5b4b9247e..3f542e8ee 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -932,7 +932,8 @@  static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
 
 static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
 				   int pairwise, int key_idx, int set_tx,
-				   const u8 *key, size_t key_len)
+				   const u8 *key, size_t key_len,
+				   enum key_flag key_flag)
 {
 	NDIS_802_11_WEP *wep;
 	size_t len;
@@ -967,7 +968,8 @@  static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
 				   enum wpa_alg alg, const u8 *addr,
 				   int key_idx, int set_tx,
 				   const u8 *seq, size_t seq_len,
-				   const u8 *key, size_t key_len)
+				   const u8 *key, size_t key_len,
+				   enum key_flag key_flag)
 {
 	struct wpa_driver_ndis_data *drv = priv;
 	size_t len, i;
@@ -993,7 +995,7 @@  static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
 
 	if (alg == WPA_ALG_WEP) {
 		return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
-					       key, key_len);
+					       key, key_len, key_flag);
 	}
 
 	len = 12 + 6 + 6 + 8 + key_len;
@@ -1075,7 +1077,7 @@  wpa_driver_ndis_associate(void *priv,
 						bcast, i,
 						i == params->wep_tx_keyidx,
 						NULL, 0, params->wep_key[i],
-						params->wep_key_len[i]);
+						params->wep_key_len[i], 0);
 		}
 	}
 
@@ -1112,7 +1114,7 @@  wpa_driver_ndis_associate(void *priv,
 			wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
 						bcast, 0, 1,
 						NULL, 0, dummy_key,
-						sizeof(dummy_key));
+						sizeof(dummy_key), 0);
 		}
 #endif /* CONFIG_WPS */
 	} else {
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 967a24225..64d16d1b4 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -3015,7 +3015,8 @@  static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
 				      enum wpa_alg alg, const u8 *addr,
 				      int key_idx, int set_tx,
 				      const u8 *seq, size_t seq_len,
-				      const u8 *key, size_t key_len)
+				      const u8 *key, size_t key_len,
+				      enum key_flag key_flag)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ifindex;
@@ -3493,7 +3494,7 @@  retry:
 					   NULL, i,
 					   i == params->wep_tx_keyidx, NULL, 0,
 					   params->wep_key[i],
-					   params->wep_key_len[i]);
+					   params->wep_key_len[i], 0);
 		if (params->wep_tx_keyidx != i)
 			continue;
 		if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
@@ -8653,11 +8654,13 @@  static int driver_nl80211_set_key(const char *ifname, void *priv,
 				  enum wpa_alg alg, const u8 *addr,
 				  int key_idx, int set_tx,
 				  const u8 *seq, size_t seq_len,
-				  const u8 *key, size_t key_len)
+				  const u8 *key, size_t key_len,
+				  enum key_flag key_flag)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx,
-					  set_tx, seq, seq_len, key, key_len);
+					  set_tx, seq, seq_len, key, key_len,
+					  key_flag);
 }
 
 
diff --git a/src/drivers/driver_openbsd.c b/src/drivers/driver_openbsd.c
index c06e75c0f..a96045121 100644
--- a/src/drivers/driver_openbsd.c
+++ b/src/drivers/driver_openbsd.c
@@ -71,7 +71,8 @@  wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa)
 static int
 wpa_driver_openbsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 	    const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
-	    size_t seq_len, const u8 *key, size_t key_len)
+	    size_t seq_len, const u8 *key, size_t key_len,
+	    enum key_flag key_flag)
 {
 	struct openbsd_driver_data *drv = priv;
 	struct ieee80211_keyavail keyavail;
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 55cf61885..f8840f848 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -209,7 +209,8 @@  static int wpa_driver_privsep_set_key(const char *ifname, void *priv,
 				      enum wpa_alg alg, const u8 *addr,
 				      int key_idx, int set_tx,
 				      const u8 *seq, size_t seq_len,
-				      const u8 *key, size_t key_len)
+				      const u8 *key, size_t key_len,
+				      enum key_flag key_flag)
 {
 	struct wpa_driver_privsep_data *drv = priv;
 	struct privsep_cmd_set_key cmd;
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 32c297138..368443f57 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -1712,7 +1712,8 @@  static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
 				       const u8 *addr, int key_idx,
 				       int set_tx, const u8 *seq,
 				       size_t seq_len,
-				       const u8 *key, size_t key_len)
+				       const u8 *key, size_t key_len,
+				       enum key_flag key_flag)
 {
 	struct wpa_driver_wext_data *drv = priv;
 	struct iwreq iwr;
@@ -1829,7 +1830,8 @@  static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
 int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 			    const u8 *addr, int key_idx,
 			    int set_tx, const u8 *seq, size_t seq_len,
-			    const u8 *key, size_t key_len)
+			    const u8 *key, size_t key_len,
+			    enum key_flag key_flag)
 {
 	struct wpa_driver_wext_data *drv = priv;
 	struct iwreq iwr;
@@ -1841,7 +1843,7 @@  int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 		   (unsigned long) seq_len, (unsigned long) key_len);
 
 	ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
-					  seq, seq_len, key, key_len);
+					  seq, seq_len, key, key_len, key_flag);
 	if (ret == 0)
 		return 0;
 
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
index b4b5960a7..66a01ce1c 100644
--- a/src/drivers/driver_wext.h
+++ b/src/drivers/driver_wext.h
@@ -55,7 +55,8 @@  int wpa_driver_wext_set_mode(void *priv, int mode);
 int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 			    const u8 *addr, int key_idx,
 			    int set_tx, const u8 *seq, size_t seq_len,
-			    const u8 *key, size_t key_len);
+			    const u8 *key, size_t key_len,
+			    enum key_flag key_flag);
 int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params);
 struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv);
 
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 704c95e68..bb8973942 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -178,7 +178,7 @@  static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
 static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 {
 	if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
-			   0, 0, NULL, 0, NULL, 0) < 0) {
+			   0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE) < 0) {
 		wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
 			   "the driver");
 		return -1;
@@ -227,8 +227,9 @@  static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 
 	wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
 		   MAC2STR(peer->addr));
-	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
-			   rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
+	if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, rsc, sizeof(rsc),
+			   peer->tpk.tk, key_len,
+			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
 			   "driver");
 		return -1;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index e8931ff08..e42e55785 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -804,7 +804,8 @@  static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
 
 
 static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
-				      const struct wpa_eapol_key *key)
+				      const struct wpa_eapol_key *key,
+				      enum key_flag key_flag)
 {
 	int keylen, rsclen;
 	enum wpa_alg alg;
@@ -849,7 +850,8 @@  static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 	}
 
 	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
-			   sm->ptk.tk, keylen) < 0) {
+			   sm->ptk.tk, keylen,
+			   KEY_FLAG_PAIRWISE | key_flag) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Failed to set PTK to the "
 			"driver (alg=%d keylen=%d bssid=" MACSTR ")",
@@ -942,7 +944,8 @@  static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
 	if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
 		if (wpa_sm_set_key(sm, gd->alg, NULL,
 				   gd->keyidx, 1, key_rsc, gd->key_rsc_len,
-				   _gtk, gd->gtk_len) < 0) {
+				   _gtk, gd->gtk_len,
+				   KEY_FLAG_GROUP_RX_TX_DEFAULT) < 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Failed to set GTK to the driver "
 				"(Group only)");
@@ -951,7 +954,7 @@  static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
 		}
 	} else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
 				  gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
-				  _gtk, gd->gtk_len) < 0) {
+				  _gtk, gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Failed to set GTK to "
 			"the driver (alg=%d keylen=%d keyidx=%d)",
@@ -1105,7 +1108,7 @@  static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
 	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) {
+			   igtk->igtk, len, KEY_FLAG_GROUP_RX) < 0) {
 		if (keyidx == 0x0400 || keyidx == 0x0500) {
 			/* Assume the AP has broken PMF implementation since it
 			 * seems to have swapped the KeyID bytes. The AP cannot
@@ -1560,7 +1563,7 @@  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))
+		if (wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX_TX))
 			goto failed;
 	}
 
@@ -4587,7 +4590,7 @@  int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
 	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
 			sm->ptk.tk, keylen);
 	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
-			   sm->ptk.tk, keylen) < 0) {
+			   sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
 			MACSTR ")",
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index ef77475cb..b0272097b 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -31,7 +31,7 @@  struct wpa_sm_ctx {
 	int (*set_key)(void *ctx, enum wpa_alg alg,
 		       const u8 *addr, int key_idx, int set_tx,
 		       const u8 *seq, size_t seq_len,
-		       const u8 *key, size_t key_len);
+		       const u8 *key, size_t key_len, enum key_flag key_flag);
 	void * (*get_network_ctx)(void *ctx);
 	int (*get_bssid)(void *ctx, u8 *bssid);
 	int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 2b8b41fa5..8a8c545d3 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -422,8 +422,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) < 0) {
+	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, sizeof(null_rsc),
+			   (u8 *) sm->ptk.tk, keylen,
+			   KEY_FLAG_PAIRWISE_RX_TX) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
 		return -1;
 	}
@@ -773,7 +774,8 @@  static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
 		os_memcpy(gtk + 24, tmp, 8);
 	}
 	if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
-			   gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
+			   gtk_elem + 3, rsc_len, gtk, keylen,
+			   KEY_FLAG_GROUP_RX) < 0) {
 		wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
 			   "driver.");
 		return -1;
@@ -840,7 +842,8 @@  static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
 			igtk_len);
 	if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
 			   broadcast_ether_addr, keyidx, 0,
-			   igtk_elem + 2, 6, igtk, igtk_len) < 0) {
+			   igtk_elem + 2, 6, igtk, igtk_len,
+			   KEY_FLAG_GROUP_RX) < 0) {
 		wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
 			   "driver.");
 		forced_memzero(igtk, sizeof(igtk));
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 8faaa85a3..634d19bfb 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -205,11 +205,12 @@  static inline void wpa_sm_reconnect(struct wpa_sm *sm)
 static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
 				 const u8 *addr, int key_idx, int set_tx,
 				 const u8 *seq, size_t seq_len,
-				 const u8 *key, size_t key_len)
+				 const u8 *key, size_t key_len,
+				 enum key_flag key_flag)
 {
 	WPA_ASSERT(sm->ctx->set_key);
 	return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
-				seq, seq_len, key, key_len);
+				seq, seq_len, key, key_len, key_flag);
 }
 
 static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
diff --git a/tests/hwsim/test_ap_ciphers.py b/tests/hwsim/test_ap_ciphers.py
index 48903ede9..c138fe571 100644
--- a/tests/hwsim/test_ap_ciphers.py
+++ b/tests/hwsim/test_ap_ciphers.py
@@ -874,7 +874,7 @@  def test_ap_wpa2_delayed_m1_m3_zero_tk(dev, apdev):
     if "OK" not in hapd.request("RESEND_M3 " + addr):
         raise Exception("RESEND_M3 failed")
 
-    if "OK" not in hapd.request("SET_KEY 3 %s %d %d %s %s" % (addr, 0, 1, 6*"00", 16*"00")):
+    if "OK" not in hapd.request("SET_KEY 3 %s %d %d %s %s %d" % (addr, 0, 1, 6*"00", 16*"00", 44)):
         raise Exception("SET_KEY failed")
     time.sleep(0.1)
     hwsim_utils.test_connectivity(dev[0], hapd, timeout=1, broadcast=False,
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 339d8198c..65dcb90f1 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -5345,15 +5345,21 @@  static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 {
 	wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
 	/* MLME-DELETEKEYS.request */
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
-	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
+			0, KEY_FLAG_GROUP);
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
+			0, KEY_FLAG_GROUP);
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
+			0, KEY_FLAG_GROUP);
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
+			0, KEY_FLAG_GROUP);
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
+			0, KEY_FLAG_GROUP);
+	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
+			0, KEY_FLAG_GROUP);
 
 	wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
-			0);
+			0, KEY_FLAG_PAIRWISE);
 	/* MLME-SETPROTECTION.request(None) */
 	wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
 				   MLME_SETPROTECTION_PROTECT_TYPE_NONE,
@@ -9351,13 +9357,15 @@  static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s)
 	 * in the driver. */
 	if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
 			    wpa_s->last_tk_key_idx, 1, zero, 6,
-			    zero, wpa_s->last_tk_len) < 0)
+			    zero, wpa_s->last_tk_len,
+			    KEY_FLAG_PAIRWISE_RX_TX) < 0)
 		return -1;
 
 	/* Set the previously configured key to reset its TSC/RSC */
 	return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
 			       wpa_s->last_tk_key_idx, 1, zero, 6,
-			       wpa_s->last_tk, wpa_s->last_tk_len);
+			       wpa_s->last_tk, wpa_s->last_tk_len,
+			       KEY_FLAG_PAIRWISE_RX_TX);
 }
 
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index f8d63a07b..11146d34a 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -147,7 +147,8 @@  static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
 				  enum wpa_alg alg, const u8 *addr,
 				  int key_idx, int set_tx,
 				  const u8 *seq, size_t seq_len,
-				  const u8 *key, size_t key_len)
+				  const u8 *key, size_t key_len,
+				  enum key_flag key_flag)
 {
 	if (alg != WPA_ALG_NONE) {
 		if (key_idx >= 0 && key_idx <= 6)
@@ -158,7 +159,8 @@  static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
 	if (wpa_s->driver->set_key) {
 		return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
 					      alg, addr, key_idx, set_tx,
-					      seq, seq_len, key, key_len);
+					      seq, seq_len, key, key_len,
+					      key_flag);
 	}
 	return -1;
 }
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 93a683551..68d063bbc 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -140,7 +140,7 @@  static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer)
 static int supp_set_key(void *ctx, enum wpa_alg alg,
 			const u8 *addr, int key_idx, int set_tx,
 			const u8 *seq, size_t seq_len,
-			const u8 *key, size_t key_len)
+			const u8 *key, size_t key_len, enum key_flag key_flag)
 {
 	struct ibss_rsn_peer *peer = ctx;
 
@@ -167,7 +167,7 @@  static int supp_set_key(void *ctx, enum wpa_alg alg,
 	if (is_broadcast_ether_addr(addr))
 		addr = peer->addr;
 	return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
-			       set_tx, seq, seq_len, key, key_len);
+			       set_tx, seq, seq_len, key, key_len, key_flag);
 }
 
 
@@ -303,7 +303,8 @@  static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
 
 
 static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
-			const u8 *addr, int idx, u8 *key, size_t key_len)
+			const u8 *addr, int idx, u8 *key, size_t key_len,
+			enum key_flag key_flag)
 {
 	struct ibss_rsn *ibss_rsn = ctx;
 	u8 seq[6];
@@ -342,7 +343,7 @@  static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 	}
 
 	return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
-			       1, seq, 6, key, key_len);
+			       1, seq, 6, key, key_len, key_flag);
 }
 
 
@@ -855,7 +856,7 @@  static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn,
 		wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer "
 			   MACSTR, MAC2STR(addr));
 		wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0,
-				NULL, 0, NULL, 0);
+				NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE);
 	}
 
 	if (peer &&
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index c7c85929f..31dd1d3c6 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -877,7 +877,8 @@  static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
 		wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
 		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
 				sta->addr, 0, 0, seq, sizeof(seq),
-				sta->mtk, sta->mtk_len);
+				sta->mtk, sta->mtk_len,
+				KEY_FLAG_PAIRWISE_RX_TX);
 
 		wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC",
 				sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
@@ -886,7 +887,8 @@  static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
 		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
 				sta->addr, sta->mgtk_key_id, 0,
 				sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
-				sta->mgtk, sta->mgtk_len);
+				sta->mgtk, sta->mgtk_len,
+				KEY_FLAG_GROUP_RX);
 
 		if (sta->igtk_len) {
 			wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC",
@@ -898,7 +900,8 @@  static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
 				wpa_cipher_to_alg(conf->mgmt_group_cipher),
 				sta->addr, sta->igtk_key_id, 0,
 				sta->igtk_rsc, sizeof(sta->igtk_rsc),
-				sta->igtk, sta->igtk_len);
+				sta->igtk, sta->igtk_len,
+				KEY_FLAG_GROUP_RX);
 		}
 	}
 
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index e730b6336..f19bfbfc6 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -100,7 +100,8 @@  static const u8 *auth_get_psk(void *ctx, const u8 *addr,
 
 
 static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
-			const u8 *addr, int idx, u8 *key, size_t key_len)
+			const u8 *addr, int idx, u8 *key, size_t key_len,
+			enum key_flag key_flag)
 {
 	struct mesh_rsn *mesh_rsn = ctx;
 	u8 seq[6];
@@ -118,7 +119,7 @@  static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 	wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
 
 	return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
-			       1, seq, 6, key, key_len);
+			       1, seq, 6, key, key_len, key_flag);
 }
 
 
@@ -196,7 +197,8 @@  static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
 		wpa_drv_set_key(rsn->wpa_s,
 				wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL,
 				rsn->igtk_key_id, 1,
-				seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
+				seq, sizeof(seq), rsn->igtk, rsn->igtk_len,
+				KEY_FLAG_GROUP_TX_DEFAULT);
 	}
 
 	/* group privacy / data frames */
@@ -204,7 +206,7 @@  static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
 			rsn->mgtk, rsn->mgtk_len);
 	wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL,
 			rsn->mgtk_key_id, 1, seq, sizeof(seq),
-			rsn->mgtk, rsn->mgtk_len);
+			rsn->mgtk, rsn->mgtk_len, KEY_FLAG_GROUP_TX_DEFAULT);
 
 	return 0;
 }
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index e6460fa4a..4f0861c99 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -142,7 +142,10 @@  int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 		set = 1;
 		wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
 				i, i == ssid->wep_tx_keyidx, NULL, 0,
-				ssid->wep_key[i], ssid->wep_key_len[i]);
+				ssid->wep_key[i], ssid->wep_key_len[i],
+				i == ssid->wep_tx_keyidx ?
+				     KEY_FLAG_GROUP_RX_TX_DEFAULT :
+				     KEY_FLAG_GROUP_RX_TX);
 	}
 
 	return set;
@@ -200,7 +203,8 @@  int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
 	/* TODO: should actually remember the previously used seq#, both for TX
 	 * and RX from each STA.. */
 
-	ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+	ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen,
+			      KEY_FLAG_GROUP_RX_TX_DEFAULT);
 	os_memset(key, 0, sizeof(key));
 	return ret;
 }
@@ -734,12 +738,12 @@  void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 		if (wpa_s->keys_cleared & BIT(i))
 			continue;
 		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
-				NULL, 0);
+				NULL, 0, KEY_FLAG_GROUP);
 	}
 	if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
 	    !is_zero_ether_addr(addr)) {
 		wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
-				0);
+				0, KEY_FLAG_PAIRWISE);
 		/* MLME-SETPROTECTION.request(None) */
 		wpa_drv_mlme_setprotection(
 			wpa_s, addr,
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 1f78e2d93..5bf2c2e4f 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -242,7 +242,9 @@  static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
 	}
 	return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
 			       unicast ? wpa_s->bssid : NULL,
-			       keyidx, unicast, NULL, 0, key, keylen);
+			       keyidx, unicast, NULL, 0, key, keylen,
+			       unicast ? KEY_FLAG_PAIRWISE_RX_TX :
+					 KEY_FLAG_GROUP_RX_TX_DEFAULT);
 }
 
 
@@ -341,7 +343,7 @@  static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol,
 			"handshake", pmk, pmk_len);
 
 	if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
-			    pmk_len)) {
+			    pmk_len, KEY_FLAG_PMK)) {
 		wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
 	}
 
@@ -498,7 +500,8 @@  static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
 static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
 				  const u8 *addr, int key_idx, int set_tx,
 				  const u8 *seq, size_t seq_len,
-				  const u8 *key, size_t key_len)
+				  const u8 *key, size_t key_len,
+				  enum key_flag key_flag)
 {
 	struct wpa_supplicant *wpa_s = _wpa_s;
 	if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
@@ -523,7 +526,7 @@  static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 	return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
-			       key, key_len);
+			       key, key_len, key_flag);
 }
 
 
@@ -1176,7 +1179,7 @@  static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
 	if (wpa_s->conf->key_mgmt_offload &&
 	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
 		return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
-				       NULL, 0, pmk, pmk_len);
+				       NULL, 0, pmk, pmk_len, KEY_FLAG_PMK);
 	else
 		return 0;
 }