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:
Jouni Malinen 2017-05-07 12:00:27 +03:00
parent 215eaa748b
commit 7d440a3bc4
3 changed files with 219 additions and 3 deletions

View file

@ -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 */

View file

@ -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

View file

@ -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);