FILS: Derive FT key hierarchy on supplicant side for FILS+FT
Derive PMK-R0 and the relevant key names when using FILS authentication for initial FT mobility domain association. Fill in the FT IEs in (Re)Association Request frame for this. Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
215eaa748b
commit
7d440a3bc4
3 changed files with 219 additions and 3 deletions
|
@ -2466,6 +2466,9 @@ void wpa_sm_deinit(struct wpa_sm *sm)
|
|||
#ifdef CONFIG_FILS_SK_PFS
|
||||
crypto_ecdh_deinit(sm->fils_ecdh);
|
||||
#endif /* CONFIG_FILS_SK_PFS */
|
||||
#ifdef CONFIG_FILS
|
||||
wpabuf_free(sm->fils_ft_ies);
|
||||
#endif /* CONFIG_FILS */
|
||||
#ifdef CONFIG_OWE
|
||||
crypto_ecdh_deinit(sm->owe_ecdh);
|
||||
#endif /* CONFIG_OWE */
|
||||
|
@ -3503,8 +3506,53 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
|
|||
os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
|
||||
|
||||
/* TODO: MDE when using FILS+FT */
|
||||
/* TODO: FTE when using FILS+FT */
|
||||
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
|
||||
struct wpa_ft_ies parse;
|
||||
|
||||
if (!elems.mdie || !elems.ftie) {
|
||||
wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (wpa_ft_parse_ies(pos, end - pos, &parse) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse.r0kh_id) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS+FT: No R0KH-ID subelem in FTE");
|
||||
goto fail;
|
||||
}
|
||||
os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
|
||||
sm->r0kh_id_len = parse.r0kh_id_len;
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
|
||||
sm->r0kh_id, sm->r0kh_id_len);
|
||||
|
||||
if (!parse.r1kh_id) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"FILS+FT: No R1KH-ID subelem in FTE");
|
||||
goto fail;
|
||||
}
|
||||
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID",
|
||||
sm->r1kh_id, FT_R1KH_ID_LEN);
|
||||
|
||||
/* TODO: Check MDE and FTE payload */
|
||||
|
||||
wpabuf_free(sm->fils_ft_ies);
|
||||
sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len +
|
||||
2 + elems.ftie_len);
|
||||
if (!sm->fils_ft_ies)
|
||||
goto fail;
|
||||
wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2,
|
||||
2 + elems.mdie_len);
|
||||
wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2,
|
||||
2 + elems.ftie_len);
|
||||
} else {
|
||||
wpabuf_free(sm->fils_ft_ies);
|
||||
sm->fils_ft_ies = NULL;
|
||||
}
|
||||
|
||||
/* PMKID List */
|
||||
if (rsn.pmkid && rsn.num_pmkid > 0) {
|
||||
|
@ -3604,7 +3652,7 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
|
|||
if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
|
||||
sm->fils_nonce, sm->fils_anonce, &sm->ptk,
|
||||
ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher,
|
||||
NULL, NULL) < 0) {
|
||||
sm->fils_ft, &sm->fils_ft_len) < 0) {
|
||||
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
|
||||
goto fail;
|
||||
}
|
||||
|
@ -3648,6 +3696,110 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
|
||||
{
|
||||
struct rsn_ie_hdr *rsnie;
|
||||
u16 capab;
|
||||
u8 *pos;
|
||||
|
||||
/* RSNIE[PMKR0Name/PMKR1Name] */
|
||||
rsnie = wpabuf_put(buf, sizeof(*rsnie));
|
||||
rsnie->elem_id = WLAN_EID_RSN;
|
||||
WPA_PUT_LE16(rsnie->version, RSN_VERSION);
|
||||
|
||||
/* Group Suite Selector */
|
||||
if (!wpa_cipher_valid_group(sm->group_cipher)) {
|
||||
wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
|
||||
sm->group_cipher);
|
||||
return -1;
|
||||
}
|
||||
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
|
||||
RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
|
||||
sm->group_cipher));
|
||||
|
||||
/* Pairwise Suite Count */
|
||||
wpabuf_put_le16(buf, 1);
|
||||
|
||||
/* Pairwise Suite List */
|
||||
if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
|
||||
wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
|
||||
sm->pairwise_cipher);
|
||||
return -1;
|
||||
}
|
||||
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
|
||||
RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
|
||||
sm->pairwise_cipher));
|
||||
|
||||
/* Authenticated Key Management Suite Count */
|
||||
wpabuf_put_le16(buf, 1);
|
||||
|
||||
/* Authenticated Key Management Suite List */
|
||||
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
|
||||
if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
|
||||
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
|
||||
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
|
||||
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
|
||||
else {
|
||||
wpa_printf(MSG_WARNING,
|
||||
"FILS+FT: Invalid key management type (%d)",
|
||||
sm->key_mgmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* RSN Capabilities */
|
||||
capab = 0;
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
|
||||
capab |= WPA_CAPABILITY_MFPC;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
wpabuf_put_le16(buf, capab);
|
||||
|
||||
/* PMKID Count */
|
||||
wpabuf_put_le16(buf, 1);
|
||||
|
||||
/* PMKID List [PMKR1Name] */
|
||||
wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)",
|
||||
sm->fils_ft, sm->fils_ft_len);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len);
|
||||
wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID",
|
||||
sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
|
||||
wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
|
||||
sm->r0kh_id, sm->r0kh_id_len);
|
||||
if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
|
||||
sm->ssid_len, sm->mobility_domain,
|
||||
sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
|
||||
sm->pmk_r0, sm->pmk_r0_name) < 0) {
|
||||
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", sm->pmk_r0, PMK_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name",
|
||||
sm->pmk_r0_name, WPA_PMK_NAME_LEN);
|
||||
wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
|
||||
MAC2STR(sm->r1kh_id));
|
||||
pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
|
||||
if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
|
||||
pos) < 0) {
|
||||
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN);
|
||||
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
|
||||
/* Management Group Cipher Suite */
|
||||
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
|
||||
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
|
||||
rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
|
||||
|
||||
struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
|
||||
size_t *kek_len, const u8 **snonce,
|
||||
const u8 **anonce,
|
||||
|
@ -3659,12 +3811,30 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
|
|||
unsigned int i;
|
||||
|
||||
len = 1000;
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
if (sm->fils_ft_ies)
|
||||
len += wpabuf_len(sm->fils_ft_ies);
|
||||
if (wpa_key_mgmt_ft(sm->key_mgmt))
|
||||
len += 256;
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
for (i = 0; hlp && i < num_hlp; i++)
|
||||
len += 10 + wpabuf_len(hlp[i]);
|
||||
buf = wpabuf_alloc(len);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
|
||||
/* MDE and FTE when using FILS+FT */
|
||||
wpabuf_put_buf(buf, sm->fils_ft_ies);
|
||||
/* RSNE with PMKR1Name in PMKID field */
|
||||
if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) {
|
||||
wpabuf_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
|
||||
/* FILS Session */
|
||||
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
|
||||
wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
|
||||
|
|
|
@ -154,6 +154,9 @@ struct wpa_sm {
|
|||
struct crypto_ecdh *fils_ecdh;
|
||||
int fils_dh_group;
|
||||
size_t fils_dh_elem_len;
|
||||
struct wpabuf *fils_ft_ies;
|
||||
u8 fils_ft[FILS_FT_MAX_LEN];
|
||||
size_t fils_ft_len;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
#ifdef CONFIG_OWE
|
||||
|
|
|
@ -1058,6 +1058,22 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
static void remove_ie(u8 *buf, size_t *len, u8 eid)
|
||||
{
|
||||
u8 *pos, *next, *end;
|
||||
|
||||
pos = (u8 *) get_ie(buf, *len, eid);
|
||||
if (pos) {
|
||||
next = pos + 2 + pos[1];
|
||||
end = buf + *len;
|
||||
*len -= 2 + pos[1];
|
||||
os_memmove(pos, next, end - next);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
|
||||
|
||||
void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
|
||||
const u8 *bssid, u16 auth_type)
|
||||
{
|
||||
|
@ -1113,6 +1129,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
|
|||
wpabuf_free(hlp[i]);
|
||||
if (!buf)
|
||||
return;
|
||||
wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements",
|
||||
wpa_s->sme.assoc_req_ie,
|
||||
wpa_s->sme.assoc_req_ie_len);
|
||||
#ifdef CONFIG_IEEE80211R
|
||||
if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
|
||||
/* Remove RSNE and MDE to allow them to be overridden
|
||||
* with FILS+FT specific values from
|
||||
* fils_build_assoc_req(). */
|
||||
remove_ie(wpa_s->sme.assoc_req_ie,
|
||||
&wpa_s->sme.assoc_req_ie_len,
|
||||
WLAN_EID_RSN);
|
||||
wpa_hexdump(MSG_DEBUG,
|
||||
"FILS: assoc_req after RSNE removal",
|
||||
wpa_s->sme.assoc_req_ie,
|
||||
wpa_s->sme.assoc_req_ie_len);
|
||||
remove_ie(wpa_s->sme.assoc_req_ie,
|
||||
&wpa_s->sme.assoc_req_ie_len,
|
||||
WLAN_EID_MOBILITY_DOMAIN);
|
||||
wpa_hexdump(MSG_DEBUG,
|
||||
"FILS: assoc_req after MDE removal",
|
||||
wpa_s->sme.assoc_req_ie,
|
||||
wpa_s->sme.assoc_req_ie_len);
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
/* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
|
||||
if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
|
||||
sizeof(wpa_s->sme.assoc_req_ie)) {
|
||||
|
@ -1125,6 +1165,9 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
|
|||
wpabuf_head(buf), wpabuf_len(buf));
|
||||
wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
|
||||
wpabuf_free(buf);
|
||||
wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements",
|
||||
wpa_s->sme.assoc_req_ie,
|
||||
wpa_s->sme.assoc_req_ie_len);
|
||||
|
||||
os_memcpy(nonces, snonce, FILS_NONCE_LEN);
|
||||
os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
|
||||
|
|
Loading…
Reference in a new issue