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) { } else if (os_strcmp(buf, "wpa") == 0) {
bss->wpa = atoi(pos); 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) { } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos); bss->wpa_group_rekey = atoi(pos);
bss->wpa_group_rekey_set = 1; 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; 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; 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_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
#wpa=2 #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 # 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 # 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 # (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} */ * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
int extended_key_id;
int wpa_key_mgmt; int wpa_key_mgmt;
enum mfp_options ieee80211w; enum mfp_options ieee80211w;
int group_mgmt_cipher; int group_mgmt_cipher;

View file

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

View file

@ -169,6 +169,7 @@ struct ft_remote_r1kh {
struct wpa_auth_config { struct wpa_auth_config {
int wpa; int wpa;
int extended_key_id;
int wpa_key_mgmt; int wpa_key_mgmt;
int wpa_pairwise; int wpa_pairwise;
int wpa_group; 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 * again after association to get the PTK configured, but that could be
* optimized by adding the STA entry earlier. * 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)) sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
return; return;

View file

@ -41,6 +41,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
os_memset(wconf, 0, sizeof(*wconf)); os_memset(wconf, 0, sizeof(*wconf));
wconf->wpa = conf->wpa; wconf->wpa = conf->wpa;
wconf->extended_key_id = conf->extended_key_id;
wconf->wpa_key_mgmt = conf->wpa_key_mgmt; wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
wconf->wpa_pairwise = conf->wpa_pairwise; wconf->wpa_pairwise = conf->wpa_pairwise;
wconf->wpa_group = conf->wpa_group; 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 #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; struct sta_info *sta;
sta = ap_get_sta(hapd, addr); sta = ap_get_sta(hapd, addr);
@ -1419,6 +1424,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
_conf.wpa_deny_ptk0_rekey = 1; _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); hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) { if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed."); wpa_printf(MSG_ERROR, "WPA initialization failed.");

View file

@ -61,6 +61,8 @@ struct wpa_state_machine {
unsigned int pmk_len; unsigned int pmk_len;
u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */ u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
struct wpa_ptk PTK; struct wpa_ptk PTK;
u8 keyidx_active;
Boolean use_ext_key_id;
Boolean PTK_valid; Boolean PTK_valid;
Boolean pairwise_set; Boolean pairwise_set;
Boolean tk_already_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) if (rsn_testing)
capab |= BIT(8) | BIT(15); capab |= BIT(8) | BIT(15);
#endif /* CONFIG_RSN_TESTING */ #endif /* CONFIG_RSN_TESTING */
if (conf->extended_key_id)
capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
WPA_PUT_LE16(pos, capab); WPA_PUT_LE16(pos, capab);
pos += 2; 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 *mdie, size_t mdie_len,
const u8 *owe_dh, size_t owe_dh_len) const u8 *owe_dh, size_t owe_dh_len)
{ {
struct wpa_auth_config *conf = &wpa_auth->conf;
struct wpa_ie_data data; struct wpa_ie_data data;
int ciphers, key_mgmt, res, version; int ciphers, key_mgmt, res, version;
u32 selector; u32 selector;
@ -944,6 +947,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
} }
#endif /* CONFIG_DPP */ #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) { if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie); os_free(sm->wpa_ie);
sm->wpa_ie = os_malloc(wpa_ie_len); sm->wpa_ie = os_malloc(wpa_ie_len);