Add BIGTK KDE and subelement similarly to IGTK

This provides the BIGTK updates to associated stations similarly to
IGTK.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2020-02-17 23:08:05 +02:00 committed by Jouni Malinen
parent 555dcd75ce
commit 16889aff40
5 changed files with 153 additions and 14 deletions

View file

@ -54,6 +54,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
size_t len;
size_t gtk_elem_len = 0;
size_t igtk_elem_len = 0;
size_t bigtk_elem_len = 0;
struct wnm_sleep_element wnmsleep_ie;
u8 *wnmtfs_ie, *oci_ie;
u8 wnmsleep_ie_len, oci_ie_len;
@ -122,8 +123,10 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#define MAX_GTK_SUBELEM_LEN 45
#define MAX_IGTK_SUBELEM_LEN 26
#define MAX_BIGTK_SUBELEM_LEN 26
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
MAX_BIGTK_SUBELEM_LEN +
oci_ie_len);
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
@ -157,10 +160,19 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
pos += igtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
(int) igtk_elem_len);
if (hapd->conf->beacon_prot) {
res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos);
if (res < 0)
goto fail;
bigtk_elem_len = res;
pos += bigtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4 bigtk_len = %d",
(int) bigtk_elem_len);
}
WPA_PUT_LE16((u8 *)
&mgmt->u.action.u.wnm_sleep_resp.keydata_len,
gtk_elem_len + igtk_elem_len);
gtk_elem_len + igtk_elem_len + bigtk_elem_len);
}
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
/* copy TFS IE here */
@ -176,7 +188,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#endif /* CONFIG_OCV */
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
igtk_elem_len + bigtk_elem_len +
wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
/* In driver, response frame should be forced to sent when STA is in
* PS mode */
@ -189,8 +202,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
/* when entering wnmsleep
* 1. pause the node in driver
* 2. mark the node so that AP won't update GTK/IGTK during
* WNM Sleep
* 2. mark the node so that AP won't update GTK/IGTK/BIGTK
* during WNM Sleep
*/
if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
@ -201,7 +214,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
}
/* when exiting wnmsleep
* 1. unmark the node
* 2. start GTK/IGTK update if MFP is not used
* 2. start GTK/IGTK/BIGTK update if MFP is not used
* 3. unpause the node in driver
*/
if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
@ -221,6 +234,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#undef MAX_GTK_SUBELEM_LEN
#undef MAX_IGTK_SUBELEM_LEN
#undef MAX_BIGTK_SUBELEM_LEN
fail:
os_free(wnmtfs_ie);
os_free(oci_ie);

View file

@ -63,6 +63,7 @@ static void wpa_group_get(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int ieee80211w_kde_len(struct wpa_state_machine *sm);
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
static const u32 eapol_key_timeout_first = 100; /* ms */
@ -2679,7 +2680,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
size_t gtk_len;
struct wpa_group *gsm;
plain = wpabuf_alloc(1000);
plain = wpabuf_alloc(1000 + ieee80211w_kde_len(sm));
if (!plain)
return NULL;
@ -2727,7 +2728,7 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
gtk, gtk_len);
wpabuf_put(plain, tmp2 - tmp);
/* IGTK KDE */
/* IGTK KDE and BIGTK KDE */
tmp = wpabuf_put(plain, 0);
tmp2 = ieee80211w_kde_add(sm, tmp);
wpabuf_put(plain, tmp2 - tmp);
@ -3105,19 +3106,25 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
static int ieee80211w_kde_len(struct wpa_state_machine *sm)
{
size_t len = 0;
if (sm->mgmt_frame_prot) {
size_t len;
len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
}
if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
}
return 0;
return len;
}
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_igtk_kde igtk;
struct wpa_bigtk_kde bigtk;
struct wpa_group *gsm = sm->group;
u8 rsc[WPA_KEY_RSC_LEN];
size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
@ -3146,6 +3153,21 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
(const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
NULL, 0);
if (!sm->wpa_auth->conf.beacon_prot)
return pos;
bigtk.keyid[0] = gsm->GN_bigtk;
bigtk.keyid[1] = 0;
if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
else
os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
(const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
NULL, 0);
return pos;
}
@ -3205,7 +3227,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
}
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
GTK[GN], IGTK, [FTIE], [TIE * 2])
GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
*/
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
@ -3978,6 +4000,36 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
return pos - start;
}
int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_group *gsm = sm->group;
u8 *start = pos;
size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/*
* BIGTK subelement:
* Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
*/
*pos++ = WNM_SLEEP_SUBELEM_BIGTK;
*pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
return 0;
pos += 6;
os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
pos += len;
wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
gsm->GN_bigtk);
wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit",
gsm->IGTK[gsm->GN_bigtk - 6], len);
return pos - start;
}
#endif /* CONFIG_WNM_AP */
@ -5074,7 +5126,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
int wpa_ie_len, secure, gtkidx, encr = 0;
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
GTK[GN], IGTK, [FTIE], [TIE * 2])
GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
*/
/* Use 0 RSC */

View file

@ -438,6 +438,7 @@ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_auth_uses_sae(struct wpa_state_machine *sm);
int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);

View file

@ -2282,6 +2282,54 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
}
static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
const u8 *kek;
size_t kek_len;
size_t bigtk_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;
}
bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
* Key[16+8] */
subelem_len = 1 + 1 + 2 + 6 + 1 + bigtk_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
pos = subelem;
*pos++ = FTIE_SUBELEM_BIGTK;
*pos++ = subelem_len - 2;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
if (aes_wrap(kek, kek_len, bigtk_len / 8,
gsm->IGTK[gsm->GN_bigtk - 6], pos)) {
wpa_printf(MSG_DEBUG,
"FT: BIGTK 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)
@ -2511,6 +2559,29 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
subelem_len += igtk_len;
os_free(igtk);
}
if (sm->mgmt_frame_prot && conf->beacon_prot) {
u8 *bigtk;
size_t bigtk_len;
u8 *nbuf;
bigtk = wpa_ft_bigtk_subelem(sm, &bigtk_len);
if (!bigtk) {
wpa_printf(MSG_DEBUG,
"FT: Failed to add BIGTK subelement");
os_free(subelem);
return NULL;
}
nbuf = os_realloc(subelem, subelem_len + bigtk_len);
if (!nbuf) {
os_free(subelem);
os_free(bigtk);
return NULL;
}
subelem = nbuf;
os_memcpy(subelem + subelem_len, bigtk, bigtk_len);
subelem_len += bigtk_len;
os_free(bigtk);
}
#ifdef CONFIG_OCV
if (wpa_auth_uses_ocv(sm)) {
struct wpa_channel_info ci;

View file

@ -1875,7 +1875,8 @@ enum wnm_sleep_mode_response_status {
/* WNM-Sleep Mode subelement IDs */
enum wnm_sleep_mode_subelement_id {
WNM_SLEEP_SUBELEM_GTK = 0,
WNM_SLEEP_SUBELEM_IGTK = 1
WNM_SLEEP_SUBELEM_IGTK = 1,
WNM_SLEEP_SUBELEM_BIGTK = 2,
};
/* Channel Switch modes (802.11h) */