[PATCH 08/21] WPA: Add CIGTK handling and installation support

Benjamin Berg benjamin at sipsolutions.net
Tue Oct 27 06:16:54 PDT 2026


From: Benjamin Berg <benjamin.berg at intel.com>

Handle the CIGTK when it is carried in an FT subelement or the RSN KDE.
Install the CIGTK when CIP is enabled for the wpa_sm.

Note that CIGTK uses bits 13 and 14 in keys_cleared to track whether a
key was uploaded.

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
Reviewed-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/common/ieee802_11_defs.h    |   1 +
 src/common/wpa_common.c         |  46 +++++++
 src/common/wpa_common.h         |  37 ++++++
 src/rsn_supp/wpa.c              | 227 ++++++++++++++++++++++++++++++++
 src/rsn_supp/wpa.h              |   4 +
 src/rsn_supp/wpa_ft.c           |  60 ++++++++-
 src/rsn_supp/wpa_i.h            |   3 +
 src/utils/common.h              |   2 +
 wpa_supplicant/ctrl_iface.c     |   6 +
 wpa_supplicant/driver_i.h       |   4 +
 wpa_supplicant/wpa_supplicant.c |   9 ++
 11 files changed, 398 insertions(+), 1 deletion(-)

diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 20edcae0b8..291695456f 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -2233,6 +2233,7 @@ enum wnm_sleep_mode_subelement_id {
 	WNM_SLEEP_SUBELEM_GTK = 0,
 	WNM_SLEEP_SUBELEM_IGTK = 1,
 	WNM_SLEEP_SUBELEM_BIGTK = 2,
+	WNM_SLEEP_SUBELEM_CIGTK = 7,
 };
 
 /* WNM notification type (IEEE Std 802.11-2020, Table 9-431) */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 264c2e164a..9adfb8a1f4 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1329,6 +1329,11 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
 			parse->bigtk = pos;
 			parse->bigtk_len = len;
 			break;
+		case FTIE_SUBELEM_CIGTK:
+			wpa_printf(MSG_DEBUG, "FT: CIGTK");
+			parse->cigtk = pos;
+			parse->cigtk_len = len;
+			break;
 		case FTIE_SUBELEM_MLO_GTK:
 			if (len < 2 + 1 + 1 + 8) {
 				wpa_printf(MSG_DEBUG,
@@ -1374,6 +1379,21 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
 			parse->mlo_bigtk[link_id] = pos;
 			parse->mlo_bigtk_len[link_id] = len;
 			break;
+		case FTIE_SUBELEM_MLO_CIGTK:
+			if (len < 2 + 6 + 1) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Too short MLO CIGTK in FTE");
+				return -1;
+			}
+			link_id = pos[2 + 6] & 0x0f;
+			wpa_printf(MSG_DEBUG, "FT: MLO CIGTK (Link ID %u)",
+				   link_id);
+			if (link_id >= MAX_NUM_MLD_LINKS)
+				break;
+			parse->valid_mlo_cigtks |= BIT(link_id);
+			parse->mlo_cigtk[link_id] = pos;
+			parse->mlo_cigtk_len[link_id] = len;
+			break;
 		default:
 			wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
 			break;
@@ -4218,6 +4238,14 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
 		return 0;
 	}
 
+	if (left > 2 && selector == RSN_KEY_DATA_CIGTK) {
+		ie->cigtk = p;
+		ie->cigtk_len = left;
+		wpa_hexdump_key(MSG_DEBUG, "WPA: CIGTK in EAPOL-Key",
+				pos, dlen);
+		return 0;
+	}
+
 	if (left >= 1 && selector == WFA_KEY_DATA_IP_ADDR_REQ) {
 		ie->ip_addr_req = p;
 		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
@@ -4311,6 +4339,24 @@ static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
 		return 0;
 	}
 
+	if (left >= RSN_MLO_CIGTK_KDE_PREFIX_LENGTH &&
+	    selector == RSN_KEY_DATA_MLO_CIGTK) {
+		link_id = u8_get_bits(p[7],
+				      RSN_MLO_CIGTK_KDE_PREFIX8_LINK_ID_MASK);
+		if (link_id >= MAX_NUM_MLD_LINKS)
+			return 2;
+
+		ie->valid_mlo_cigtks |= BIT(link_id);
+		ie->mlo_cigtk[link_id] = p;
+		ie->mlo_cigtk_len[link_id] = left;
+		ret = os_snprintf(title, sizeof(title),
+				  "RSN: Link ID %u - MLO CIGTK KDE in EAPOL-Key",
+				  link_id);
+		if (!os_snprintf_error(sizeof(title), ret))
+			wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+		return 0;
+	}
+
 	if (left >= RSN_MLO_LINK_KDE_FIXED_LENGTH &&
 	    selector == RSN_KEY_DATA_MLO_LINK) {
 		link_id = (p[0] & RSN_MLO_LINK_KDE_LI_LINK_ID_MASK) >>
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 158f79afd1..dcbb13fe0e 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -158,6 +158,8 @@ WPA_CIPHER_BIP_CMAC_256)
 #define RSN_KEY_DATA_MLO_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
 #define RSN_KEY_DATA_MLO_LINK RSN_SELECTOR(0x00, 0x0f, 0xac, 19)
 #define RSN_KEY_DATA_SAE_PW_IDS RSN_SELECTOR(0x00, 0x0f, 0xac, 25)
+#define RSN_KEY_DATA_CIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 24)
+#define RSN_KEY_DATA_MLO_CIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 26)
 
 #define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
 #define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
@@ -184,6 +186,7 @@ WPA_CIPHER_BIP_CMAC_256)
 #define WPA_IGTK_MAX_LEN 32
 #define WPA_BIGTK_LEN 16
 #define WPA_BIGTK_MAX_LEN 32
+#define WPA_CIGTK_LEN 32
 
 
 /* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
@@ -309,6 +312,10 @@ struct wpa_bigtk {
 	size_t bigtk_len;
 };
 
+struct wpa_cigtk {
+	u8 cigtk[WPA_CIGTK_LEN];
+};
+
 /* WPA IE version 1
  * 00-50-f2:1 (OUI:OUI type)
  * 0x01 0x00 (version; little endian)
@@ -390,6 +397,13 @@ struct wpa_bigtk_kde {
 	u8 bigtk[WPA_BIGTK_MAX_LEN];
 } STRUCT_PACKED;
 
+#define WPA_CIGTK_KDE_PREFIX_LEN (offsetofend(struct wpa_cigtk_kde, pn))
+struct wpa_cigtk_kde {
+	u8 key_info;
+	u8 pn[RSN_PN_LEN];
+	u8 cigtk[WPA_CIGTK_LEN];
+} STRUCT_PACKED;
+
 #define RSN_MLO_GTK_KDE_PREFIX_LENGTH		(1 + RSN_PN_LEN)
 #define RSN_MLO_GTK_KDE_PREFIX0_KEY_ID_MASK	0x03
 #define RSN_MLO_GTK_KDE_PREFIX0_TX		0x04
@@ -416,6 +430,17 @@ struct rsn_mlo_bigtk_kde {
 	u8 bigtk[WPA_BIGTK_MAX_LEN];
 } STRUCT_PACKED;
 
+#define RSN_MLO_CIGTK_KDE_KEY_IDX_MASK		0x01
+#define RSN_MLO_CIGTK_KDE_PREFIX_LENGTH		\
+	(offsetofend(struct rsn_mlo_cigtk_kde, prefix8))
+#define RSN_MLO_CIGTK_KDE_PREFIX8_LINK_ID_MASK	0xF0
+struct rsn_mlo_cigtk_kde {
+	u8 key_info;
+	u8 pn[RSN_PN_LEN];
+	u8 prefix8;
+	u8 cigtk[WPA_CIGTK_LEN];
+} STRUCT_PACKED;
+
 #define RSN_MLO_LINK_KDE_FIXED_LENGTH		(1 + ETH_ALEN)
 #define RSN_MLO_LINK_KDE_LINK_INFO_INDEX	0
 #define RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT	0
@@ -465,6 +490,8 @@ struct rsn_ftie_sha512 {
 #define FTIE_SUBELEM_MLO_GTK 8
 #define FTIE_SUBELEM_MLO_IGTK 9
 #define FTIE_SUBELEM_MLO_BIGTK 10
+#define FTIE_SUBELEM_CIGTK 12
+#define FTIE_SUBELEM_MLO_CIGTK 14
 
 struct rsn_rdie {
 	u8 id;
@@ -624,6 +651,8 @@ struct wpa_ft_ies {
 	size_t igtk_len;
 	const u8 *bigtk;
 	size_t bigtk_len;
+	const u8 *cigtk;
+	size_t cigtk_len;
 #ifdef CONFIG_OCV
 	const u8 *oci;
 	size_t oci_len;
@@ -643,6 +672,9 @@ struct wpa_ft_ies {
 	u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK subelements */
 	const u8 *mlo_bigtk[MAX_NUM_MLD_LINKS];
 	size_t mlo_bigtk_len[MAX_NUM_MLD_LINKS];
+	u16 valid_mlo_cigtks; /* bitmap of valid link CIGTK subelements */
+	const u8 *mlo_cigtk[MAX_NUM_MLD_LINKS];
+	size_t mlo_cigtk_len[MAX_NUM_MLD_LINKS];
 
 	struct wpabuf *fte_buf;
 };
@@ -705,6 +737,8 @@ struct wpa_eapol_ie_parse {
 	size_t igtk_len;
 	const u8 *bigtk;
 	size_t bigtk_len;
+	const u8 *cigtk;
+	size_t cigtk_len;
 	const u8 *sae_pw_ids;
 	size_t sae_pw_ids_len;
 	const u8 *mdie;
@@ -773,6 +807,9 @@ struct wpa_eapol_ie_parse {
 	u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK KDEs */
 	const u8 *mlo_bigtk[MAX_NUM_MLD_LINKS];
 	size_t mlo_bigtk_len[MAX_NUM_MLD_LINKS];
+	u16 valid_mlo_cigtks; /* bitmap of valid link CIGTK KDEs */
+	const u8 *mlo_cigtk[MAX_NUM_MLD_LINKS];
+	size_t mlo_cigtk_len[MAX_NUM_MLD_LINKS];
 	u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */
 	const u8 *mlo_link[MAX_NUM_MLD_LINKS];
 	size_t mlo_link_len[MAX_NUM_MLD_LINKS];
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5a05a991b9..1e1a7045c8 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1856,6 +1856,47 @@ static int wpa_supplicant_install_bigtk(struct wpa_sm *sm,
 }
 
 
+static int wpa_supplicant_install_cigtk(struct wpa_sm *sm,
+					const struct wpa_cigtk_kde *cigtk,
+					int wnm_sleep)
+{
+	u8 keyidx = cigtk->key_info & 0x1;
+
+	STATIC_ASSERT(sizeof(sm->cigtk.cigtk) == WPA_CIGTK_LEN);
+
+	/* Detect possible key reinstallation */
+	if (os_memcmp(sm->cigtk.cigtk, cigtk->cigtk,
+		      sizeof(cigtk->cigtk)) == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Not reinstalling already in-use CIGTK to the driver (keyidx=%d)",
+			keyidx);
+		return  0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"WPA: CIGTK keyid %d pn " COMPACT_MACSTR,
+		keyidx, MAC2STR(cigtk->pn));
+	wpa_hexdump_key(MSG_DEBUG, "WPA: CIGTK", cigtk->cigtk, WPA_CIGTK_LEN);
+	if (wpa_sm_set_key(sm, -1, WPA_ALG_GCMP_256,
+			   broadcast_ether_addr,
+			   keyidx, 0, cigtk->pn, sizeof(cigtk->pn),
+			   cigtk->cigtk, WPA_CIGTK_LEN,
+			   KEY_FLAG_CIGTK | KEY_FLAG_RX) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: Failed to configure CIGTK to the driver");
+		return -1;
+	}
+
+	if (wnm_sleep)
+		os_memcpy(sm->cigtk_wnm_sleep.cigtk, cigtk->cigtk,
+			  WPA_CIGTK_LEN);
+	else
+		os_memcpy(sm->cigtk.cigtk, cigtk->cigtk, WPA_CIGTK_LEN);
+
+	return 0;
+}
+
+
 static int wpa_supplicant_install_mlo_igtk(struct wpa_sm *sm, u8 link_id,
 					   const struct rsn_mlo_igtk_kde *igtk,
 					   int wnm_sleep)
@@ -1972,6 +2013,54 @@ wpa_supplicant_install_mlo_bigtk(struct wpa_sm *sm, u8 link_id,
 }
 
 
+static int
+wpa_supplicant_install_mlo_cigtk(struct wpa_sm *sm, u8 link_id,
+				 const struct rsn_mlo_cigtk_kde *cigtk,
+				 int wnm_sleep)
+{
+	u16 keyidx = u8_get_bits(cigtk->key_info,
+				 RSN_MLO_CIGTK_KDE_KEY_IDX_MASK);
+
+	STATIC_ASSERT(sizeof(sm->mlo.links[link_id].cigtk.cigtk) ==
+		      WPA_CIGTK_LEN);
+
+	/* Detect possible key reinstallation */
+	if (os_memcmp(sm->mlo.links[link_id].cigtk.cigtk, cigtk->cigtk,
+		      sizeof(cigtk->cigtk)) == 0) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"RSN: Not reinstalling already in-use CIGTK to the driver (link_id=%d keyidx=%d)",
+			link_id, keyidx);
+		return  0;
+	}
+
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"RSN: MLO Link %u CIGTK keyid %d pn " COMPACT_MACSTR,
+		link_id, keyidx, MAC2STR(cigtk->pn));
+	wpa_hexdump_link_key(MSG_DEBUG, link_id, "RSN: CIGTK", cigtk->cigtk,
+			     WPA_CIGTK_LEN);
+
+	if (wpa_sm_set_key(sm, link_id,
+			   WPA_ALG_GCMP_256,
+			   broadcast_ether_addr, keyidx, 0, cigtk->pn,
+			   sizeof(cigtk->pn), cigtk->cigtk, WPA_CIGTK_LEN,
+			   KEY_FLAG_CIGTK | KEY_FLAG_RX) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN: Failed to configure MLO Link %d CIGTK to the driver",
+			link_id);
+		return -1;
+	}
+
+	if (wnm_sleep)
+		os_memcpy(sm->mlo.links[link_id].cigtk_wnm_sleep.cigtk,
+			  cigtk->cigtk, sizeof(cigtk->cigtk));
+	else
+		os_memcpy(sm->mlo.links[link_id].cigtk.cigtk, cigtk->cigtk,
+			  sizeof(cigtk->cigtk));
+
+	return 0;
+}
+
+
 static int _mlo_ieee80211w_set_keys(struct wpa_sm *sm, u8 link_id,
 				    struct wpa_eapol_ie_parse *ie)
 {
@@ -2064,6 +2153,63 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
 }
 
 
+static int _mlo_cip_set_keys(struct wpa_sm *sm, u8 link_id,
+			     struct wpa_eapol_ie_parse *ie)
+{
+	if (ie->mlo_cigtk[link_id]) {
+		if (ie->mlo_cigtk_len[link_id] !=
+		    RSN_MLO_CIGTK_KDE_PREFIX_LENGTH + WPA_CIGTK_LEN)
+			return -1;
+
+		if (wpa_supplicant_install_mlo_cigtk(
+			    sm, link_id,
+			    (const struct rsn_mlo_cigtk_kde *)
+			    ie->mlo_cigtk[link_id],
+			    0) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+
+static int mlo_cip_set_keys(struct wpa_sm *sm,
+			    struct wpa_eapol_ie_parse *ie)
+{
+	u8 i;
+
+	if (!sm->cip)
+		return 0;
+
+	for_each_link(sm->mlo.valid_links, i) {
+		if (_mlo_cip_set_keys(sm, i, ie))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int cip_set_keys(struct wpa_sm *sm,
+			struct wpa_eapol_ie_parse *ie)
+{
+	if (!sm->cip)
+		return 0;
+
+	if (ie->cigtk) {
+		const struct wpa_cigtk_kde *cigtk;
+
+		if (ie->cigtk_len != WPA_CIGTK_KDE_PREFIX_LEN + WPA_CIGTK_LEN)
+			return -1;
+
+		cigtk = (const struct wpa_cigtk_kde *) ie->cigtk;
+		if (wpa_supplicant_install_cigtk(sm, cigtk, 0) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static void wpa_report_ie_mismatch(struct wpa_sm *sm,
 				   const char *reason, const u8 *src_addr,
 				   const u8 *wpa_ie, size_t wpa_ie_len,
@@ -2755,6 +2901,26 @@ static int wpa_validate_mlo_ieee80211w_kdes(struct wpa_sm *sm,
 }
 
 
+static int wpa_validate_mlo_cip_kdes(struct wpa_sm *sm,
+				     u8 link_id,
+				     struct wpa_eapol_ie_parse *ie)
+{
+	if (!sm->cip)
+		return 0;
+
+	if (ie->mlo_cigtk[link_id] &&
+	    ie->mlo_cigtk_len[link_id] != RSN_MLO_CIGTK_KDE_PREFIX_LENGTH +
+	    WPA_CIGTK_LEN) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSN MLO: Invalid CIGTK KDE length %lu for link ID %u",
+			(unsigned long) ie->mlo_cigtk_len[link_id], link_id);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 static void wpa_supplicant_process_3_of_4_wpa(struct wpa_sm *sm,
 					      const struct wpa_eapol_key *key,
 					      u16 ver, const u8 *key_data,
@@ -2963,6 +3129,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 		    wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
 		    wpa_validate_mlo_ieee80211w_kdes(sm, i, &ie) < 0)
 			goto failed;
+
+		if (sm->cip &&
+		    wpa_validate_mlo_cip_kdes(sm, i, &ie) < 0)
+			goto failed;
 	}
 
 #ifdef CONFIG_IEEE80211R
@@ -3138,6 +3308,13 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 		goto failed;
 	}
 
+	if ((mlo && mlo_cip_set_keys(sm, &ie) < 0) ||
+	    (!mlo && cip_set_keys(sm, &ie) < 0)) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure CIGTK");
+		goto failed;
+	}
+
 	if (mlo || sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
 		wpa_supplicant_key_neg_complete(sm, sm->bssid,
 						key_info & WPA_KEY_INFO_SECURE);
@@ -3327,6 +3504,10 @@ static void wpa_supplicant_process_mlo_1_of_2(struct wpa_sm *sm,
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"MLO RSN: Failed to configure MLO IGTK");
 
+	if (mlo_cip_set_keys(sm, &ie) < 0)
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"MLO RSN: Failed to configure MLO CIGTK");
+
 	for_each_link(sm->mlo.valid_links, i) {
 		/*
 		 * AP may send group keys for subset of the all links during
@@ -3587,6 +3768,10 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"RSN: Failed to configure IGTK");
 
+	if (cip_set_keys(sm, &ie) < 0)
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure CIGTK");
+
 	key_rsc = key->key_rsc;
 	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
 		key_rsc = null_rsc;
@@ -5317,6 +5502,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 	case WPA_PARAM_SECURITY_PROFILE_ACTIVE:
 		sm->security_profile_active = !!value;
 		break;
+	case WPA_PARAM_CONTROL_INTEGRITY_PROTOCOL:
+		sm->cip = !!value;
+		break;
 	default:
 		break;
 	}
@@ -6279,6 +6467,12 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 		if (sm->beacon_prot &&
 		    wpa_supplicant_install_bigtk(sm, bigtk, 1) < 0)
 			return -1;
+	} else if (subelem_id == WNM_SLEEP_SUBELEM_CIGTK) {
+		const struct wpa_cigtk_kde *cigtk;
+
+		cigtk = (const struct wpa_cigtk_kde *) (buf + 2);
+		if (sm->cip && wpa_supplicant_install_cigtk(sm, cigtk, 1) < 0)
+			return -1;
 	} else {
 		wpa_printf(MSG_DEBUG, "Unknown element id");
 		return -1;
@@ -7379,6 +7573,11 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
 		goto fail;
 	}
 
+	if (cip_set_keys(sm, &kde) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set CIGTK");
+		goto fail;
+	}
+
 	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 	if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
@@ -7706,6 +7905,12 @@ bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm)
 }
 
 
+bool wpa_sm_uses_cip(struct wpa_sm *sm)
+{
+	return sm ? sm->cip : false;
+}
+
+
 struct rsn_pmksa_cache * wpa_sm_get_pmksa_cache(struct wpa_sm *sm)
 {
 	return sm ? sm->pmksa : NULL;
@@ -7787,6 +7992,24 @@ static int mlo_ieee80211w_set_keys_for_new_links(struct wpa_sm *sm,
 }
 
 
+static int mlo_cip_set_keys_for_new_links(struct wpa_sm *sm,
+					  struct wpa_eapol_ie_parse *ie,
+					  u16 added_links_bitmap)
+{
+	int i;
+
+	if (!sm->cip)
+		return 0;
+
+	for_each_link(added_links_bitmap, i) {
+		if (_mlo_cip_set_keys(sm, i, ie))
+			return -1;
+	}
+
+	return 0;
+}
+
+
 static int wpa_supplicant_install_mlo_gtk_keys(struct wpa_sm *sm,
 					       struct wpa_eapol_ie_parse *ie,
 					       u16 added_links_bitmap)
@@ -7831,6 +8054,10 @@ int wpa_sm_install_mlo_group_keys(struct wpa_sm *sm, const u8 *key_data,
 						  added_links_bitmap) < 0)
 		return -1;
 
+	if (mlo_cip_set_keys_for_new_links(sm, &ie,
+					   added_links_bitmap) < 0)
+		return -1;
+
 	return 0;
 }
 
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index c61ca16a46..38e28541a0 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -148,6 +148,7 @@ enum wpa_sm_conf_params {
 	WPA_PARAM_ASSOC_ENC,
 	WPA_PARAM_PMKSA_CACHING_PRIVACY,
 	WPA_PARAM_SECURITY_PROFILE_ACTIVE,
+	WPA_PARAM_CONTROL_INTEGRITY_PROTOCOL,
 };
 
 enum wpa_rsn_override {
@@ -188,6 +189,8 @@ struct wpa_sm_link {
 	struct wpa_igtk igtk_wnm_sleep;
 	struct wpa_bigtk bigtk;
 	struct wpa_bigtk bigtk_wnm_sleep;
+	struct wpa_cigtk cigtk;
+	struct wpa_cigtk cigtk_wnm_sleep;
 };
 
 struct wpa_sm_mlo {
@@ -300,6 +303,7 @@ int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo);
 void wpa_sm_set_driver_bss_selection(struct wpa_sm *sm,
 				     bool driver_bss_selection);
 bool wpa_sm_uses_spp_amsdu(struct wpa_sm *sm);
+bool wpa_sm_uses_cip(struct wpa_sm *sm);
 
 #else /* CONFIG_NO_WPA */
 
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index fb46fa2f40..9f608e0a94 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -1024,6 +1024,62 @@ static int wpa_ft_process_bigtk_subelem(struct wpa_sm *sm, const u8 *bigtk_elem,
 }
 
 
+static int wpa_ft_process_cigtk_subelem(struct wpa_sm *sm, const u8 *cigtk_elem,
+					size_t cigtk_elem_len)
+{
+	u8 cigtk[WPA_CIGTK_LEN];
+	u8 keyidx;
+	const u8 *kek;
+	size_t kek_len;
+
+	if (!sm->cip || !cigtk_elem)
+		return 0;
+
+	if (wpa_key_mgmt_fils(sm->key_mgmt)) {
+		kek = sm->ptk.kek2;
+		kek_len = sm->ptk.kek2_len;
+	} else {
+		kek = sm->ptk.kek;
+		kek_len = sm->ptk.kek_len;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: Received CIGTK in Reassoc Resp",
+			cigtk_elem, cigtk_elem_len);
+
+	if (cigtk_elem_len != 1 + 6 + WPA_CIGTK_LEN + 8) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: Invalid CIGTK sub-elem length %lu",
+			   (unsigned long) cigtk_elem_len);
+		return -1;
+	}
+	if (aes_unwrap(kek, kek_len, WPA_CIGTK_LEN / 8, cigtk_elem + 7,
+		       cigtk)) {
+		wpa_printf(MSG_WARNING,
+			   "FT: AES unwrap failed - could not decrypt CIGTK");
+		return -1;
+	}
+
+	/* KeyInfo[1] | CIPN[6] | Key[32+8] */
+
+	keyidx = u8_get_bits(cigtk_elem[0], 0x1);
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: CIGTK from Reassoc Resp", cigtk,
+			WPA_CIGTK_LEN);
+	if (wpa_sm_set_key(sm, -1, WPA_ALG_GCMP_256,
+			   broadcast_ether_addr, keyidx, 0,
+			   cigtk_elem + 1, 6, cigtk, WPA_CIGTK_LEN,
+			   KEY_FLAG_CIGTK | KEY_FLAG_RX) < 0) {
+		wpa_printf(MSG_WARNING,
+			   "WPA: Failed to set CIGTK to the driver");
+		forced_memzero(cigtk, sizeof(cigtk));
+		return -1;
+	}
+	forced_memzero(cigtk, sizeof(cigtk));
+
+	return 0;
+}
+
+
 int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 				 size_t ies_len, const u8 *src_addr)
 {
@@ -1256,7 +1312,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 
 	if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0 ||
 	    wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0 ||
-	    wpa_ft_process_bigtk_subelem(sm, parse.bigtk, parse.bigtk_len) < 0)
+	    wpa_ft_process_bigtk_subelem(sm, parse.bigtk,
+					 parse.bigtk_len) < 0 ||
+	    wpa_ft_process_cigtk_subelem(sm, parse.cigtk, parse.cigtk_len) < 0)
 		goto fail;
 
 	if (sm->set_ptk_after_assoc) {
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 72b26d1d9e..71bdb4ec59 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -42,6 +42,8 @@ struct wpa_sm {
 	struct wpa_igtk igtk_wnm_sleep;
 	struct wpa_bigtk bigtk;
 	struct wpa_bigtk bigtk_wnm_sleep;
+	struct wpa_cigtk cigtk;
+	struct wpa_cigtk cigtk_wnm_sleep;
 
 	struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
 
@@ -75,6 +77,7 @@ struct wpa_sm {
 	int wpa_rsc_relaxation;
 	int owe_ptk_workaround;
 	int beacon_prot;
+	bool cip;
 	int ext_key_id; /* whether Extended Key ID is enabled */
 	int use_ext_key_id; /* whether Extended Key ID has been detected
 			     * to be used */
diff --git a/src/utils/common.h b/src/utils/common.h
index eb434ebb5c..abe6b25b21 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -606,6 +606,8 @@ int * int_array_dup(const int *a);
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
+#define STATIC_ASSERT(cond) (void)sizeof(char[(cond) ? 1 : -1])
+
 void str_clear_free(char *str);
 void bin_clear_free(void *bin, size_t len);
 
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 44d400020c..397a91c293 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -6198,6 +6198,12 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 			0, KEY_FLAG_GROUP);
 	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
 			0, KEY_FLAG_GROUP);
+	if (wpa_drv_get_cip_supported(wpa_s)) {
+		wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 0, 0, NULL, 0,
+				NULL, 0, KEY_FLAG_CIGTK);
+		wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, NULL, 1, 0, NULL, 0,
+				NULL, 0, KEY_FLAG_CIGTK);
+	}
 
 	wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0,
 			NULL, 0, KEY_FLAG_PAIRWISE);
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 559be7aad6..731e045789 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -184,9 +184,13 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, int link_id,
 		 * Extended Key ID--a unicast key. Use bit 15 for
 		 * the pairwise keyidx 1 which is hopefully high enough
 		 * to not clash with future extensions.
+		 *
+		 * Use bit 13 and 14 for CIGTK (key_idx is 0 or 1).
 		 */
 		if (key_idx == 1 && (key_flag & KEY_FLAG_PAIRWISE))
 			wpa_s->keys_cleared &= ~BIT(15);
+		else if (key_flag & KEY_FLAG_CIGTK)
+			wpa_s->keys_cleared &= ~BIT(key_idx + 13);
 		else if (key_idx >= 0 && key_idx <= 5)
 			wpa_s->keys_cleared &= ~BIT(key_idx);
 		else
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 1bc91afcb9..3f7ea41f0b 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -942,6 +942,15 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 			MLME_SETPROTECTION_PROTECT_TYPE_NONE,
 			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 	}
+	/* CIGTK Key ID is tracked in bit 13 and 14 */
+	if (~wpa_s->keys_cleared & (BIT(13) | BIT(14))) {
+		if (!(wpa_s->keys_cleared & BIT(13)))
+			wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 0, 0,
+					NULL, 0, NULL, 0, KEY_FLAG_CIGTK);
+		if (!(wpa_s->keys_cleared & BIT(14)))
+			wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, addr, 1, 0,
+					NULL, 0, NULL, 0, KEY_FLAG_CIGTK);
+	}
 	wpa_s->keys_cleared = (u32) -1;
 }
 
-- 
2.56.0




More information about the Hostap mailing list