[PATCH 13/21] AP: WPA: Add CIGTK handling for CIP

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


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

This does not yet add everything, but the key will be installed and
included in the KDE and FT subelements if enabled.

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
Reviewed-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/ap/hostapd.h       |   2 +
 src/ap/wpa_auth.c      | 231 +++++++++++++++++++++++++++++++++++------
 src/ap/wpa_auth.h      |   9 +-
 src/ap/wpa_auth_ft.c   |  67 ++++++++++++
 src/ap/wpa_auth_glue.c |   8 ++
 src/ap/wpa_auth_i.h    |   6 ++
 src/ap/wpa_auth_ie.c   |   5 +
 7 files changed, 293 insertions(+), 35 deletions(-)

diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 6ce29653b2..b4859979e5 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -414,6 +414,8 @@ struct hostapd_data {
 	int last_bigtk_key_idx;
 	u8 last_bigtk[WPA_BIGTK_MAX_LEN];
 	size_t last_bigtk_len;
+	int last_cigtk_key_idx;
+	u8 last_cigtk[WPA_CIGTK_LEN];
 
 	bool force_backlog_bytes;
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 3d773d0cbf..9e1af3cb2a 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -70,7 +70,9 @@ static void wpa_group_put_vlan(struct wpa_authenticator *wpa_auth,
 			       int vlan_id);
 #endif /* CONFIG_IEEE80211BE */
 static int ieee80211w_kde_len(struct wpa_state_machine *sm);
+static int cip_kde_len(struct wpa_state_machine *sm);
 static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
+static u8 * cip_kde_add(struct wpa_state_machine *sm, u8 *pos);
 static int wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
 				struct wpa_group *group);
 
@@ -911,11 +913,17 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
 	}
 #endif /* CONFIG_P2P */
 
-	if (conf->tx_bss_auth && conf->beacon_prot) {
-		conf->tx_bss_auth->non_tx_beacon_prot = true;
-		if (!conf->tx_bss_auth->conf.group_mgmt_cipher)
-			conf->tx_bss_auth->conf.group_mgmt_cipher =
-				conf->group_mgmt_cipher;
+	if (conf->tx_bss_auth && (conf->beacon_prot || conf->cip)) {
+		if (conf->beacon_prot) {
+			conf->tx_bss_auth->non_tx_beacon_prot = true;
+
+			if (!conf->tx_bss_auth->conf.group_mgmt_cipher)
+				conf->tx_bss_auth->conf.group_mgmt_cipher =
+					conf->group_mgmt_cipher;
+		}
+
+		if (conf->cip)
+			conf->tx_bss_auth->non_tx_cip = true;
 	}
 
 	return wpa_auth;
@@ -2738,6 +2746,8 @@ static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
 			wpa_auth->conf.tx_bss_auth->group;
 		tx_group->bigtk_set = false;
 		tx_group->bigtk_configured = false;
+		tx_group->cigtk_set = false;
+		tx_group->cigtk_configured = false;
 	}
 
 	/*
@@ -3608,7 +3618,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
 	size_t plain_len;
 	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
 
-	plain_len = 1000 + ieee80211w_kde_len(sm);
+	plain_len = 1000 + ieee80211w_kde_len(sm) + cip_kde_len(sm);
 	if (conf->transition_disable)
 		plain_len += 2 + RSN_SELECTOR_LEN + 1;
 	plain = wpabuf_alloc(plain_len);
@@ -3670,6 +3680,11 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
 		wpabuf_put(plain, tmp2 - tmp);
 	}
 
+	/* CIGTK KDE */
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = cip_kde_add(sm, tmp);
+	wpabuf_put(plain, tmp2 - tmp);
+
 	*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
 
 #ifdef CONFIG_OCV
@@ -4296,6 +4311,19 @@ static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 }
 
 
+static int cip_kde_len(struct wpa_state_machine *sm)
+{
+	size_t len = 0;
+
+	if (sm->cip) {
+		len += 2 + RSN_SELECTOR_LEN + WPA_CIGTK_KDE_PREFIX_LEN;
+		len += WPA_CIGTK_LEN;
+	}
+
+	return len;
+}
+
+
 static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 {
 	struct wpa_igtk_kde igtk;
@@ -4367,6 +4395,49 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 }
 
 
+static u8 * cip_kde_add(struct wpa_state_machine *sm, u8 *pos)
+{
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	struct wpa_cigtk_kde cigtk;
+	struct wpa_group *gsm = sm->group;
+	u8 rsc[WPA_KEY_RSC_LEN];
+
+#ifdef CONFIG_IEEE80211BE
+	if (sm->mld_assoc_link_id >= 0)
+		return pos; /* Use per-link MLO KDEs instead */
+#endif /* CONFIG_IEEE80211BE */
+
+	if (!sm->cip)
+		return pos;
+
+	if (gsm->vlan_id) {
+		wpa_printf(MSG_DEBUG, "Fetch CIGTK from default VLAN");
+		gsm = wpa_auth->group;
+	}
+
+	if (wpa_auth->conf.tx_bss_auth) {
+		wpa_printf(MSG_DEBUG, "Fetch CIGTK from TX BSS");
+		wpa_auth = wpa_auth->conf.tx_bss_auth;
+		gsm = wpa_auth->group;
+	}
+
+	cigtk.key_info = gsm->GN_cigtk;
+	if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+	    wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_cigtk, rsc) < 0)
+		os_memset(cigtk.pn, 0, sizeof(cigtk.pn));
+	else
+		os_memcpy(cigtk.pn, rsc, sizeof(cigtk.pn));
+	os_memcpy(cigtk.cigtk, gsm->CIGTK[gsm->GN_cigtk], WPA_CIGTK_LEN);
+	pos = wpa_add_kde(pos, RSN_KEY_DATA_CIGTK,
+			  (const u8 *) &cigtk,
+			  WPA_CIGTK_KDE_PREFIX_LEN + WPA_CIGTK_LEN,
+			  NULL, 0);
+	forced_memzero(&cigtk, sizeof(cigtk));
+
+	return pos;
+}
+
+
 static int ocv_oci_len(struct wpa_state_machine *sm)
 {
 #ifdef CONFIG_OCV
@@ -4444,14 +4515,14 @@ static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
 void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
 			      struct wpa_auth_ml_link_key_info *info,
 			      bool mgmt_frame_prot, bool beacon_prot,
-			      bool rekey, int vlan_id)
+			      bool cip, bool rekey, int vlan_id)
 {
 	struct wpa_group *gsm = a->group;
 	u8 rsc[WPA_KEY_RSC_LEN];
 
 	wpa_printf(MSG_DEBUG,
-		   "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u VLAN ID:%d",
-		   info->link_id, mgmt_frame_prot, beacon_prot, vlan_id);
+		   "MLD: Get group key info: link_id=%u, IGTK=%u, BIGTK=%u, VLAN ID:%d, CIGTK=%u",
+		   info->link_id, mgmt_frame_prot, beacon_prot, vlan_id, cip);
 
 	if (vlan_id)
 		gsm = wpa_select_vlan_wpa_group(gsm, vlan_id);
@@ -4465,6 +4536,17 @@ void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
 	else
 		os_memcpy(info->pn, rsc, sizeof(info->pn));
 
+	if (cip) {
+		info->cigtkidx = gsm->GN_cigtk;
+		info->cigtk = gsm->CIGTK[gsm->GN_cigtk];
+
+		if (rekey ||
+		    wpa_auth_get_seqnum(a, NULL, gsm->GN_cigtk, rsc) < 0)
+			os_memset(info->cipn, 0, sizeof(info->cipn));
+		else
+			os_memcpy(info->cipn, rsc, sizeof(info->cipn));
+	}
+
 	if (!mgmt_frame_prot)
 		return;
 
@@ -4547,13 +4629,19 @@ size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm, u16 req_links)
 		kde_len += KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1;
 		kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
 
-		if (!wpa_auth->conf.beacon_prot)
-			continue;
+		if (wpa_auth->conf.beacon_prot) {
+			/* MLO BIGTK KDE
+			 * Header + Key ID + BIPN + LinkID + BIGTK */
+			kde_len += KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1;
+			kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+		}
 
-		/* MLO BIGTK KDE
-		 * Header + Key ID + BIPN + LinkID + BIGTK */
-		kde_len += KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1;
-		kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+		if (wpa_auth->conf.cip) {
+			/* MLO CIGTK KDE
+			 * Header + Key Info + CIPN + Prefix8 + CIGTK */
+			kde_len += KDE_HDR_LEN;
+			kde_len += sizeof(struct rsn_mlo_cigtk_kde);
+		}
 	}
 
 	wpa_printf(MSG_DEBUG, "MLO Group KDEs len = %zu", kde_len);
@@ -4579,6 +4667,7 @@ u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos, u8 *end,
 	 */
 	ml_key_info.mgmt_frame_prot = sm->mgmt_frame_prot;
 	ml_key_info.beacon_prot = sm->wpa_auth->conf.beacon_prot;
+	ml_key_info.cip = sm->cip;
 
 	for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
 		if (!sm->mld_links[link_id].valid)
@@ -4734,6 +4823,39 @@ u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos, u8 *end,
 		pos += ml_key_info.links[i].igtk_len;
 	}
 
+	/* Add MLO CIGTK KDEs */
+	for (i = 0; i < ml_key_info.n_mld_links; i++) {
+		link_id = ml_key_info.links[i].link_id;
+
+		if (!sm->mld_links[link_id].valid ||
+		    !ml_key_info.links[i].cigtk)
+			continue;
+
+		wpa_printf(MSG_DEBUG, "RSN: MLO CIGTK: link=%u", link_id);
+		wpa_hexdump_key(MSG_DEBUG, "RSN: MLO CIGTK",
+				ml_key_info.links[i].cigtk, 32);
+
+		*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+		*pos++ = RSN_SELECTOR_LEN + 1 + 1 +
+			sizeof(ml_key_info.links[i].cipn) + 32;
+
+		RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_CIGTK);
+		pos += RSN_SELECTOR_LEN;
+
+		/* Add the Key Info */
+		*pos++ = ml_key_info.links[i].cigtkidx;
+
+		/* Add the CIPN */
+		os_memcpy(pos, ml_key_info.links[i].cipn,
+			  sizeof(ml_key_info.links[i].cipn));
+		pos += sizeof(ml_key_info.links[i].cipn);
+
+		*pos++ = ml_key_info.links[i].link_id << 4;
+
+		os_memcpy(pos, ml_key_info.links[i].cigtk, 32);
+		pos += 32;
+	}
+
 	wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %td", pos - start);
 	return pos;
 }
@@ -5064,7 +5186,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 	}
 
 	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
-	   GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
+	   GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2], [CIGTK])
 	 */
 	os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
@@ -5217,7 +5339,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 		}
 	}
 
-	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm) +
+		cip_kde_len(sm);
 
 	if (sm->use_ext_key_id)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2;
@@ -5307,6 +5430,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 	pos = ieee80211w_kde_add(sm, pos);
 	if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0)
 		goto done;
+	pos = cip_kde_add(sm, pos);
 
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -5793,7 +5917,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
 
 	if (sm->wpa == WPA_VERSION_WPA2 && !is_mld) {
 		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
-			ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+			ieee80211w_kde_len(sm) + ocv_oci_len(sm) +
+			cip_kde_len(sm);
 		kde_buf = os_malloc(kde_len);
 		if (!kde_buf)
 			return;
@@ -5809,6 +5934,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
 			os_free(kde_buf);
 			return;
 		}
+		pos = cip_kde_add(sm, pos);
 		kde_len = pos - kde;
 #ifdef CONFIG_IEEE80211BE
 	} else if (sm->wpa == WPA_VERSION_WPA2 && is_mld) {
@@ -6013,27 +6139,37 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 				group->IGTK[group->GN_igtk - 4], len);
 	}
 
-	/* Skip BIGTK generation for groups with a non-zero VLAN ID since only
-	 * a single BIGTK is shared for all VLANs in a BSS. */
+	/* Skip BIGTK/CIGTK generation for groups with a non-zero VLAN ID since
+	 * only a single BIGTK/CIGTK is shared for all VLANs in a BSS. */
 	if (group->vlan_id)
 		return ret;
 
-	if (!wpa_auth->non_tx_beacon_prot &&
-	     !wpa_auth_pmf_enabled(conf))
-		return ret;
+	if (wpa_auth->conf.tx_bss_auth) {
+		wpa_auth = wpa_auth->conf.tx_bss_auth;
+		group = wpa_auth->group;
+		wpa_printf(MSG_DEBUG, "Set up BIGTK/CIGTK for TX BSS");
+	}
+
+	if (conf->cip || wpa_auth->non_tx_cip) {
+		os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+		inc_byte_array(group->Counter, WPA_NONCE_LEN);
+		if (wpa_gmk_to_gtk(group->GMK, "CIGTK key expansion",
+				   wpa_auth->addr, group->GNonce,
+				   group->CIGTK[group->GN_cigtk],
+				   WPA_CIGTK_LEN) < 0)
+			ret = -1;
+		group->cigtk_set = true;
+		group->cigtk_configured = false;
+		wpa_hexdump_key(MSG_DEBUG, "CIGTK",
+				group->CIGTK[group->GN_cigtk],
+				WPA_CIGTK_LEN);
+	}
 
 	/* Skip BIGTK generation if neither the TX BSS nor any of the non-TX BSS
 	 * enable beacon protection */
 	if (!wpa_auth->non_tx_beacon_prot && !conf->beacon_prot)
 		return ret;
 
-	if (wpa_auth->conf.tx_bss_auth) {
-		group = wpa_auth->conf.tx_bss_auth->group;
-		if (group->bigtk_set)
-			return ret;
-		wpa_printf(MSG_DEBUG, "Set up BIGTK for TX BSS");
-	}
-
 	len = wpa_cipher_key_len(conf->group_mgmt_cipher);
 	os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
 	inc_byte_array(group->Counter, WPA_NONCE_LEN);
@@ -6067,6 +6203,8 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
 	group->GM_igtk = 5;
 	group->GN_bigtk = 6;
 	group->GM_bigtk = 7;
+	group->GN_cigtk = 0;
+	group->GM_cigtk = 1;
 	/* GTK[GN] = CalcGTK() */
 	wpa_gtk_update(wpa_auth, group);
 }
@@ -6295,6 +6433,9 @@ static int wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
 	tmp = group->GM_bigtk;
 	group->GM_bigtk = group->GN_bigtk;
 	group->GN_bigtk = tmp;
+	tmp = group->GM_cigtk;
+	group->GM_cigtk = group->GN_cigtk;
+	group->GN_cigtk = tmp;
 	/* "GKeyDoneStations = GNoStations" is done in more robust way by
 	 * counting the STAs that are marked with GUpdateStationKeys instead of
 	 * including all STAs that could be in not-yet-completed state. */
@@ -6363,7 +6504,8 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 	if (ret || group->vlan_id)
 		return ret;
 
-	/* Switch to transmitting BSSID for BIGTK */
+
+	/* Switch to transmitting BSSID for BIGTK and CIGTK */
 	if (wpa_auth->conf.tx_bss_auth) {
 		wpa_auth = wpa_auth->conf.tx_bss_auth;
 		group = wpa_auth->group;
@@ -6379,6 +6521,18 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 			group->bigtk_configured = true;
 	}
 
+	if (group->cigtk_set && !group->cigtk_configured) {
+		if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+				     WPA_ALG_GCMP_256,
+				     broadcast_ether_addr, group->GN_cigtk,
+				     group->CIGTK[group->GN_cigtk],
+				     WPA_CIGTK_LEN,
+				     KEY_FLAG_CIGTK | KEY_FLAG_TX) < 0)
+			return -1;
+		else
+			group->cigtk_configured = true;
+	}
+
 	return ret;
 }
 
@@ -6583,6 +6737,10 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
 			tmp = group->GM_bigtk;
 			group->GM_bigtk = group->GN_bigtk;
 			group->GN_bigtk = tmp;
+
+			tmp = group->GM_cigtk;
+			group->GM_cigtk = group->GN_cigtk;
+			group->GN_cigtk = tmp;
 		}
 		wpa_gtk_update(wpa_auth, group);
 		wpa_group_config_group_keys(wpa_auth, group);
@@ -7691,7 +7849,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 	u8 hdr[2];
 
 	/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
-	   GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
+	   GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2], [CIGTK])
 	 */
 
 	/* Use 0 RSC */
@@ -7742,7 +7900,8 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 		}
 	}
 
-	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm) +
+		cip_kde_len(sm);
 
 	if (sm->use_ext_key_id)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2;
@@ -7845,6 +8004,8 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 	}
 #endif /* CONFIG_IEEE80211R_AP */
 
+	pos = cip_kde_add(sm, pos);
+
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) |
 		       (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len,
@@ -7880,7 +8041,8 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
 	gtk = gsm->GTK[gsm->GN - 1];
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
-			ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+			ieee80211w_kde_len(sm) + ocv_oci_len(sm) +
+			cip_kde_len(sm);
 		kde_buf = os_malloc(kde_len);
 		if (!kde_buf)
 			return -1;
@@ -7903,6 +8065,7 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
 			os_free(kde_buf);
 			return -1;
 		}
+		pos = cip_kde_add(sm, pos);
 		kde_len = pos - kde;
 	} else {
 		kde = gtk;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 9e5e38459e..629f64dfc8 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -333,6 +333,7 @@ struct wpa_auth_config {
 	int rsn_override_mlo_compat;
 
 	bool spp_amsdu;
+	bool cip;
 
 	u64 rsnxe_capab_mask;
 
@@ -358,6 +359,7 @@ struct wpa_auth_ml_key_info {
 	unsigned int n_mld_links;
 	bool mgmt_frame_prot;
 	bool beacon_prot;
+	bool cip;
 
 	struct wpa_auth_ml_link_key_info {
 		u8 link_id;
@@ -375,6 +377,10 @@ struct wpa_auth_ml_key_info {
 		u8 bigtkidx;
 		const u8 *bigtk;
 		u8 bipn[6];
+
+		u8 cigtkidx;
+		const u8 *cigtk;
+		u8 cipn[6];
 	} links[MAX_NUM_MLD_LINKS];
 };
 
@@ -492,6 +498,7 @@ int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
 		      const u8 *osen_ie, size_t osen_ie_len);
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
 int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm);
+int wpa_auth_uses_cip(struct wpa_state_machine *sm);
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
 int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
 struct wpa_state_machine *
@@ -728,7 +735,7 @@ void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
 void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
 			      struct wpa_auth_ml_link_key_info *info,
 			      bool mgmt_frame_prot, bool beacon_prot,
-			      bool rekey, int vlan_id);
+			      bool cip, bool rekey, int vlan_id);
 
 void wpa_release_link_auth_ref(struct wpa_state_machine *sm, u8 link_id,
 			       bool rejected);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e12f2ad8e8..d27df90501 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2404,6 +2404,50 @@ static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
 }
 
 
+static u8 * wpa_ft_cigtk_subelem(struct wpa_state_machine *sm, size_t *len)
+{
+	u8 *subelem, *pos;
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	struct wpa_group *gsm = wpa_auth->group;
+	size_t subelem_len;
+	const u8 *kek, *cigtk;
+	size_t kek_len;
+
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		kek = sm->PTK.kek2;
+		kek_len = sm->PTK.kek2_len;
+	} else {
+		kek = sm->PTK.kek;
+		kek_len = sm->PTK.kek_len;
+	}
+
+	/* Sub-elem ID[1] | Length[1] | KeyInfo | CIPN[6] | Key[32+8] */
+	subelem_len = 1 + 1 + 1 + 6 + WPA_CIGTK_LEN + 8;
+	subelem = os_zalloc(subelem_len);
+	if (subelem == NULL)
+		return NULL;
+
+	pos = subelem;
+	*pos++ = FTIE_SUBELEM_CIGTK;
+	*pos++ = subelem_len - 2;
+	*pos++ = u8_get_bits(gsm->GN_cigtk, RSN_MLO_CIGTK_KDE_KEY_IDX_MASK);
+
+	wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_cigtk, pos);
+	pos += 6;
+	cigtk = gsm->CIGTK[gsm->GN_cigtk];
+	if (aes_wrap(kek, kek_len, WPA_CIGTK_LEN / 8, cigtk, pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "FT: CIGTK subelem encryption failed: kek_len=%d",
+			   (int) kek_len);
+		os_free(subelem);
+		return NULL;
+	}
+
+	*len = subelem_len;
+	return subelem;
+}
+
+
 static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
 				u8 *pos, u8 *end, u8 id, u8 descr_count,
 				const u8 *ies, size_t ies_len)
@@ -2670,6 +2714,29 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 			subelem_len += bigtk_len;
 			os_free(bigtk);
 		}
+		if (sm->cip) {
+			u8 *cigtk;
+			size_t cigtk_len;
+			u8 *nbuf;
+
+			cigtk = wpa_ft_cigtk_subelem(sm, &cigtk_len);
+			if (!cigtk) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Failed to add CIGTK subelement");
+				os_free(subelem);
+				return NULL;
+			}
+			nbuf = os_realloc(subelem, subelem_len + cigtk_len);
+			if (!nbuf) {
+				os_free(subelem);
+				os_free(cigtk);
+				return NULL;
+			}
+			subelem = nbuf;
+			os_memcpy(subelem + subelem_len, cigtk, cigtk_len);
+			subelem_len += cigtk_len;
+			os_free(cigtk);
+		}
 #ifdef CONFIG_OCV
 		if (wpa_auth_uses_ocv(sm)) {
 			struct wpa_channel_info ci;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9da2e6d206..9208b57d86 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -661,6 +661,12 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 				os_memcpy(hapd->last_bigtk, key, key_len);
 			hapd->last_bigtk_len = key_len;
 		}
+	} else if (key_flag & KEY_FLAG_CIGTK) {
+		if (idx == 0 || idx == 1) {
+			hapd->last_cigtk_key_idx = idx;
+			if (key)
+				os_memcpy(hapd->last_cigtk, key, key_len);
+		}
 	} else {
 		hapd->last_gtk_alg = alg;
 		hapd->last_gtk_key_idx = idx;
@@ -1755,6 +1761,7 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
 						 &info->links[i],
 						 info->mgmt_frame_prot,
 						 info->beacon_prot,
+						 info->cip,
 						 rekey, vlan_id);
 			continue;
 		}
@@ -1767,6 +1774,7 @@ static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
 						 &info->links[i],
 						 info->mgmt_frame_prot,
 						 info->beacon_prot,
+						 info->cip,
 						 rekey, vlan_id);
 			link_bss_found = true;
 			break;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index a1b34ee0a2..7a7330b34a 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -99,6 +99,7 @@ struct wpa_state_machine {
 	unsigned int is_wnmsleep:1;
 	unsigned int pmkid_set:1;
 	unsigned int spp_amsdu:1;
+	unsigned int cip:1;
 
 	unsigned int ptkstart_without_success;
 
@@ -228,10 +229,14 @@ struct wpa_group {
 	bool reject_4way_hs_for_entropy;
 	u8 IGTK[2][WPA_IGTK_MAX_LEN];
 	u8 BIGTK[2][WPA_IGTK_MAX_LEN];
+	u8 CIGTK[2][WPA_CIGTK_LEN];
 	int GN_igtk, GM_igtk;
 	int GN_bigtk, GM_bigtk;
+	int GN_cigtk, GM_cigtk;
 	bool bigtk_set;
 	bool bigtk_configured;
+	bool cigtk_set;
+	bool cigtk_configured;
 	/* Number of references except those in struct wpa_group->next */
 	unsigned int references;
 	unsigned int num_setup_iface;
@@ -268,6 +273,7 @@ struct wpa_authenticator {
 	struct wpa_ft_pmk_cache *ft_pmk_cache;
 
 	bool non_tx_beacon_prot;
+	bool non_tx_cip;
 
 #ifdef CONFIG_P2P
 	struct bitfield *ip_pool;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 6b72eb3b11..0cc319ec77 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1530,6 +1530,11 @@ int wpa_auth_uses_spp_amsdu(struct wpa_state_machine *sm)
 	return sm ? sm->spp_amsdu : 0;
 }
 
+int wpa_auth_uses_cip(struct wpa_state_machine *sm)
+{
+	return sm ? sm->cip : 0;
+}
+
 #ifdef CONFIG_OCV
 
 void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
-- 
2.56.0




More information about the Hostap mailing list