AP: Support Extended Key ID

Support Extended Key ID in hostapd according to IEEE Std 802.11-2016.

Extended Key ID allows to rekey pairwise keys without the otherwise
unavoidable MPDU losses on a busy link. The standard is fully backward
compatible, allowing an AP to serve STAs with and without Extended Key
ID support in the same BSS.

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
This commit is contained in:
Alexander Wetzel 2020-03-20 20:04:31 +01:00 committed by Jouni Malinen
parent 9efac01020
commit 862aac1fcd
10 changed files with 130 additions and 12 deletions

View file

@ -2869,6 +2869,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
} else if (os_strcmp(buf, "wpa") == 0) {
bss->wpa = atoi(pos);
} else if (os_strcmp(buf, "extended_key_id") == 0) {
int val = atoi(pos);
if (bss->extended_key_id < 0 || bss->extended_key_id > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid extended_key_id=%d; allowed range 0..2",
line, bss->extended_key_id);
return 1;
}
bss->extended_key_id = val;
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos);
bss->wpa_group_rekey_set = 1;

View file

@ -1295,6 +1295,14 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
pos += ret;
}
if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->extended_key_id) {
ret = os_snprintf(pos, end - pos, "extended_key_id=%d\n",
hapd->conf->extended_key_id);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}

View file

@ -1510,6 +1510,17 @@ own_ip_addr=127.0.0.1
# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
#wpa=2
# Extended Key ID support for Individually Addressed frames
#
# Extended Key ID allows to rekey PTK keys without the impacts the "normal"
# PTK rekeying with only a single Key ID 0 has. It can only be used when the
# driver supports it and RSN/WPA2 is used with a CCMP/GCMP pairwise cipher.
#
# 0 = force off, i.e., use only Key ID 0 (default)
# 1 = enable and use Extended Key ID support when possible
# 2 = identical to 1 but start with Key ID 1 when possible
#extended_key_id=0
# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
# (8..63 characters) that will be converted to PSK. This conversion uses SSID

View file

@ -354,6 +354,7 @@ struct hostapd_bss_config {
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
int extended_key_id;
int wpa_key_mgmt;
enum mfp_options ieee80211w;
int group_mgmt_cipher;

View file

@ -781,7 +781,7 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
if (sm == NULL)
return;
if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
if (!sm->use_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_INFO,
"WPA: PTK0 rekey not allowed, disconnect " MACSTR,
MAC2STR(sm->addr));
@ -790,6 +790,8 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
sm->disconnect_reason =
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
} else {
if (sm->use_ext_key_id)
sm->keyidx_active ^= 1; /* flip Key ID */
sm->PTKRequest = TRUE;
sm->PTK_valid = 0;
}
@ -1754,6 +1756,11 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
0, KEY_FLAG_PAIRWISE))
wpa_printf(MSG_DEBUG,
"RSN: PTK removal from the driver failed");
if (sm->wpa_auth->conf.extended_key_id && sm->use_ext_key_id &&
wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 1, NULL,
0, KEY_FLAG_PAIRWISE))
wpa_printf(MSG_DEBUG,
"RSN: PTK Key ID 1 removal from the driver failed");
sm->pairwise_set = FALSE;
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
@ -1812,16 +1819,23 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
sm->Init = FALSE;
sm->AuthenticationRequest = TRUE;
break;
} else if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
}
if (!sm->use_ext_key_id &&
sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_INFO,
"WPA: PTK0 rekey not allowed, disconnect "
MACSTR, MAC2STR(sm->addr));
sm->Disconnect = TRUE;
/* Try to encourage the STA reconnect */
/* Try to encourage the STA to reconnect */
sm->disconnect_reason =
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
break;
}
if (sm->use_ext_key_id)
sm->keyidx_active ^= 1; /* flip Key ID */
if (sm->GUpdateStationKeys) {
/*
* Reauthentication cancels the pending group key
@ -3261,6 +3275,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
u8 *wpa_ie;
int secure, gtkidx, encr = 0;
u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
u8 hdr[2];
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = FALSE;
@ -3317,6 +3332,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 3/4 msg of 4-Way Handshake");
if (sm->wpa == WPA_VERSION_WPA2) {
if (sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
wpa_auth_set_key(sm->wpa_auth, 0,
wpa_cipher_to_alg(sm->pairwise),
sm->addr,
sm->keyidx_active, sm->PTK.tk,
wpa_cipher_key_len(sm->pairwise),
KEY_FLAG_PAIRWISE_RX)) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
/* WPA2 send GTK in the 4-way handshake */
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
@ -3357,6 +3384,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
}
kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (sm->use_ext_key_id)
kde_len += 2 + RSN_SELECTOR_LEN + 2;
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
@ -3392,10 +3423,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos += elen;
}
#endif /* CONFIG_IEEE80211R_AP */
if (gtk) {
u8 hdr[2];
hdr[0] = gtkidx & 0x03;
hdr[1] = 0;
if (sm->use_ext_key_id) {
hdr[0] = sm->keyidx_active & 0x01;
pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
}
if (gtk) {
hdr[0] = gtkidx & 0x03;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
}
@ -3478,9 +3514,17 @@ SM_STATE(WPA_PTK, PTKINITDONE)
if (sm->Pair) {
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,
KEY_FLAG_PAIRWISE_RX_TX)) {
int res;
if (sm->use_ext_key_id)
res = wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
sm->keyidx_active, NULL, 0,
KEY_FLAG_PAIRWISE_RX_TX_MODIFY);
else
res = wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr,
0, sm->PTK.tk, klen,
KEY_FLAG_PAIRWISE_RX_TX);
if (res) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
@ -5167,6 +5211,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int wpa_ie_len, secure, gtkidx, encr = 0;
u8 hdr[2];
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
@ -5219,6 +5264,10 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
}
kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (sm->use_ext_key_id)
kde_len += 2 + RSN_SELECTOR_LEN + 2;
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
@ -5251,10 +5300,15 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
pos += elen;
}
#endif /* CONFIG_IEEE80211R_AP */
if (gtk) {
u8 hdr[2];
hdr[0] = gtkidx & 0x03;
hdr[1] = 0;
if (sm->use_ext_key_id) {
hdr[0] = sm->keyidx_active & 0x01;
pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
}
if (gtk) {
hdr[0] = gtkidx & 0x03;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
}

View file

@ -169,6 +169,7 @@ struct ft_remote_r1kh {
struct wpa_auth_config {
int wpa;
int extended_key_id;
int wpa_key_mgmt;
int wpa_pairwise;
int wpa_group;

View file

@ -2775,7 +2775,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
* again after association to get the PTK configured, but that could be
* optimized by adding the STA entry earlier.
*/
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
return;

View file

@ -41,6 +41,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
os_memset(wconf, 0, sizeof(*wconf));
wconf->wpa = conf->wpa;
wconf->extended_key_id = conf->extended_key_id;
wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
wconf->wpa_pairwise = conf->wpa_pairwise;
wconf->wpa_group = conf->wpa_group;
@ -433,7 +434,11 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
}
#ifdef CONFIG_TESTING_OPTIONS
if (addr && !is_broadcast_ether_addr(addr)) {
if (key_flag & KEY_FLAG_MODIFY) {
/* We are updating an already installed key. Don't overwrite
* the already stored key information with zeros.
*/
} else if (addr && !is_broadcast_ether_addr(addr)) {
struct sta_info *sta;
sta = ap_get_sta(hapd, addr);
@ -1419,6 +1424,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
_conf.wpa_deny_ptk0_rekey = 1;
}
if (_conf.extended_key_id &&
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
else
_conf.extended_key_id = 0;
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed.");

View file

@ -61,6 +61,8 @@ struct wpa_state_machine {
unsigned int pmk_len;
u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
struct wpa_ptk PTK;
u8 keyidx_active;
Boolean use_ext_key_id;
Boolean PTK_valid;
Boolean pairwise_set;
Boolean tk_already_set;

View file

@ -297,6 +297,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
if (rsn_testing)
capab |= BIT(8) | BIT(15);
#endif /* CONFIG_RSN_TESTING */
if (conf->extended_key_id)
capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
WPA_PUT_LE16(pos, capab);
pos += 2;
@ -553,6 +555,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
const u8 *mdie, size_t mdie_len,
const u8 *owe_dh, size_t owe_dh_len)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
struct wpa_ie_data data;
int ciphers, key_mgmt, res, version;
u32 selector;
@ -944,6 +947,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
#endif /* CONFIG_DPP */
if (conf->extended_key_id && sm->wpa == WPA_VERSION_WPA2 &&
sm->pairwise != WPA_CIPHER_TKIP &&
(data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
sm->use_ext_key_id = TRUE;
if (conf->extended_key_id == 2 &&
!wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_fils(sm->wpa_key_mgmt))
sm->keyidx_active = 1;
else
sm->keyidx_active = 0;
wpa_printf(MSG_DEBUG,
"RSN: Extended Key ID supported (start with %d)",
sm->keyidx_active);
} else {
sm->use_ext_key_id = FALSE;
}
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie);
sm->wpa_ie = os_malloc(wpa_ie_len);