Browse Source

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>
Jouni Malinen 7 years ago
parent
commit
7d440a3bc4
3 changed files with 219 additions and 3 deletions
  1. 173 3
      src/rsn_supp/wpa.c
  2. 3 0
      src/rsn_supp/wpa_i.h
  3. 43 0
      wpa_supplicant/sme.c

+ 173 - 3
src/rsn_supp/wpa.c

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

+ 3 - 0
src/rsn_supp/wpa_i.h

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

+ 43 - 0
wpa_supplicant/sme.c

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