wpa_ft.c revision 252726
1132718Skan/*
2132718Skan * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
3169689Skan * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4132718Skan *
5132718Skan * This software may be distributed under the terms of the BSD license.
6132718Skan * See README for more details.
7132718Skan */
8132718Skan
9132718Skan#include "includes.h"
10132718Skan
11132718Skan#include "common.h"
12132718Skan#include "crypto/aes_wrap.h"
13132718Skan#include "crypto/random.h"
14132718Skan#include "common/ieee802_11_defs.h"
15132718Skan#include "common/ieee802_11_common.h"
16132718Skan#include "wpa.h"
17132718Skan#include "wpa_i.h"
18132718Skan
19132718Skan#ifdef CONFIG_IEEE80211R
20132718Skan
21132718Skanint wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
22132718Skan		      const struct wpa_eapol_key *key,
23169689Skan		      struct wpa_ptk *ptk, size_t ptk_len)
24169689Skan{
25132718Skan	u8 ptk_name[WPA_PMK_NAME_LEN];
26132718Skan	const u8 *anonce = key->key_nonce;
27132718Skan
28132718Skan	if (sm->xxkey_len == 0) {
29132718Skan		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
30132718Skan			   "derivation");
31132718Skan		return -1;
32132718Skan	}
33132718Skan
34132718Skan	wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
35132718Skan			  sm->ssid_len, sm->mobility_domain,
36132718Skan			  sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
37132718Skan			  sm->pmk_r0, sm->pmk_r0_name);
38132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
39132718Skan	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
40132718Skan		    sm->pmk_r0_name, WPA_PMK_NAME_LEN);
41132718Skan	wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
42132718Skan			  sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
43132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
44132718Skan	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
45169689Skan		    WPA_PMK_NAME_LEN);
46169689Skan	wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
47132718Skan			  sm->bssid, sm->pmk_r1_name,
48132718Skan			  (u8 *) ptk, ptk_len, ptk_name);
49132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
50132718Skan	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
51132718Skan
52132718Skan	return 0;
53132718Skan}
54132718Skan
55132718Skan
56132718Skan/**
57132718Skan * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
58132718Skan * @sm: Pointer to WPA state machine data from wpa_sm_init()
59132718Skan * @ies: Association Response IEs or %NULL to clear FT parameters
60132718Skan * @ies_len: Length of ies buffer in octets
61132718Skan * Returns: 0 on success, -1 on failure
62132718Skan */
63132718Skanint wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
64132718Skan{
65132718Skan	struct wpa_ft_ies ft;
66132718Skan
67132718Skan	if (sm == NULL)
68132718Skan		return 0;
69132718Skan
70132718Skan	if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
71132718Skan		return -1;
72132718Skan
73132718Skan	if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
74132718Skan		return -1;
75132718Skan
76132718Skan	if (ft.mdie) {
77132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
78132718Skan			    ft.mdie, MOBILITY_DOMAIN_ID_LEN);
79132718Skan		os_memcpy(sm->mobility_domain, ft.mdie,
80132718Skan			  MOBILITY_DOMAIN_ID_LEN);
81132718Skan		sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
82132718Skan		wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
83132718Skan			   sm->mdie_ft_capab);
84132718Skan	} else
85132718Skan		os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
86132718Skan
87132718Skan	if (ft.r0kh_id) {
88132718Skan		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
89132718Skan			    ft.r0kh_id, ft.r0kh_id_len);
90132718Skan		os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
91132718Skan		sm->r0kh_id_len = ft.r0kh_id_len;
92132718Skan	} else {
93132718Skan		/* FIX: When should R0KH-ID be cleared? We need to keep the
94132718Skan		 * old R0KH-ID in order to be able to use this during FT. */
95132718Skan		/*
96132718Skan		 * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
97132718Skan		 * sm->r0kh_id_len = 0;
98132718Skan		 */
99169689Skan	}
100169689Skan
101169689Skan	if (ft.r1kh_id) {
102169689Skan		wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
103169689Skan			    ft.r1kh_id, FT_R1KH_ID_LEN);
104132718Skan		os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
105132718Skan	} else
106132718Skan		os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
107132718Skan
108132718Skan	os_free(sm->assoc_resp_ies);
109132718Skan	sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
110132718Skan	if (sm->assoc_resp_ies) {
111132718Skan		u8 *pos = sm->assoc_resp_ies;
112132718Skan		if (ft.mdie) {
113132718Skan			os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
114132718Skan			pos += ft.mdie_len + 2;
115132718Skan		}
116132718Skan		if (ft.ftie) {
117132718Skan			os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
118132718Skan			pos += ft.ftie_len + 2;
119132718Skan		}
120132718Skan		sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
121132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
122132718Skan			    "(Re)Association Response",
123169689Skan			    sm->assoc_resp_ies, sm->assoc_resp_ies_len);
124169689Skan	}
125132718Skan
126169689Skan	return 0;
127169689Skan}
128169689Skan
129169689Skan
130169689Skan/**
131169689Skan * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
132169689Skan * @sm: Pointer to WPA state machine data from wpa_sm_init()
133169689Skan * @len: Buffer for returning the length of the IEs
134169689Skan * @anonce: ANonce or %NULL if not yet available
135169689Skan * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
136169689Skan * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
137169689Skan * @target_ap: Target AP address
138169689Skan * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
139132718Skan * @ric_ies_len: Length of ric_ies buffer in octets
140132718Skan * @ap_mdie: Mobility Domain IE from the target AP
141132718Skan * Returns: Pointer to buffer with IEs or %NULL on failure
142132718Skan *
143132718Skan * Caller is responsible for freeing the returned buffer with os_free();
144132718Skan */
145132718Skanstatic u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
146132718Skan			       const u8 *anonce, const u8 *pmk_name,
147132718Skan			       const u8 *kck, const u8 *target_ap,
148132718Skan			       const u8 *ric_ies, size_t ric_ies_len,
149132718Skan			       const u8 *ap_mdie)
150132718Skan{
151132718Skan	size_t buf_len;
152132718Skan	u8 *buf, *pos, *ftie_len, *ftie_pos;
153132718Skan	struct rsn_mdie *mdie;
154132718Skan	struct rsn_ftie *ftie;
155132718Skan	struct rsn_ie_hdr *rsnie;
156132718Skan	u16 capab;
157132718Skan
158132718Skan	sm->ft_completed = 0;
159132718Skan
160132718Skan	buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
161132718Skan		2 + sm->r0kh_id_len + ric_ies_len + 100;
162132718Skan	buf = os_zalloc(buf_len);
163132718Skan	if (buf == NULL)
164132718Skan		return NULL;
165132718Skan	pos = buf;
166132718Skan
167132718Skan	/* RSNIE[PMKR0Name/PMKR1Name] */
168132718Skan	rsnie = (struct rsn_ie_hdr *) pos;
169132718Skan	rsnie->elem_id = WLAN_EID_RSN;
170132718Skan	WPA_PUT_LE16(rsnie->version, RSN_VERSION);
171132718Skan	pos = (u8 *) (rsnie + 1);
172132718Skan
173132718Skan	/* Group Suite Selector */
174132718Skan	if (sm->group_cipher != WPA_CIPHER_CCMP &&
175132718Skan	    sm->group_cipher != WPA_CIPHER_GCMP &&
176132718Skan	    sm->group_cipher != WPA_CIPHER_TKIP) {
177132718Skan		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
178132718Skan			   sm->group_cipher);
179132718Skan		os_free(buf);
180132718Skan		return NULL;
181132718Skan	}
182132718Skan	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
183169689Skan						  sm->group_cipher));
184132718Skan	pos += RSN_SELECTOR_LEN;
185132718Skan
186132718Skan	/* Pairwise Suite Count */
187132718Skan	WPA_PUT_LE16(pos, 1);
188132718Skan	pos += 2;
189132718Skan
190132718Skan	/* Pairwise Suite List */
191132718Skan	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
192132718Skan		wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
193132718Skan			   sm->pairwise_cipher);
194169689Skan		os_free(buf);
195161651Skan		return NULL;
196132718Skan	}
197132718Skan	RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
198132718Skan						  sm->pairwise_cipher));
199132718Skan	pos += RSN_SELECTOR_LEN;
200132718Skan
201132718Skan	/* Authenticated Key Management Suite Count */
202132718Skan	WPA_PUT_LE16(pos, 1);
203132718Skan	pos += 2;
204132718Skan
205132718Skan	/* Authenticated Key Management Suite List */
206132718Skan	if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
207132718Skan		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
208132718Skan	else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
209132718Skan		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
210132718Skan	else {
211132718Skan		wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
212132718Skan			   sm->key_mgmt);
213132718Skan		os_free(buf);
214132718Skan		return NULL;
215132718Skan	}
216132718Skan	pos += RSN_SELECTOR_LEN;
217132718Skan
218132718Skan	/* RSN Capabilities */
219132718Skan	capab = 0;
220132718Skan#ifdef CONFIG_IEEE80211W
221132718Skan	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
222132718Skan		capab |= WPA_CAPABILITY_MFPC;
223132718Skan#endif /* CONFIG_IEEE80211W */
224132718Skan	WPA_PUT_LE16(pos, capab);
225132718Skan	pos += 2;
226132718Skan
227132718Skan	/* PMKID Count */
228132718Skan	WPA_PUT_LE16(pos, 1);
229132718Skan	pos += 2;
230132718Skan
231132718Skan	/* PMKID List [PMKR0Name/PMKR1Name] */
232132718Skan	os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
233132718Skan	pos += WPA_PMK_NAME_LEN;
234132718Skan
235132718Skan#ifdef CONFIG_IEEE80211W
236132718Skan	if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
237132718Skan		/* Management Group Cipher Suite */
238132718Skan		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
239132718Skan		pos += RSN_SELECTOR_LEN;
240132718Skan	}
241132718Skan#endif /* CONFIG_IEEE80211W */
242132718Skan
243132718Skan	rsnie->len = (pos - (u8 *) rsnie) - 2;
244132718Skan
245132718Skan	/* MDIE */
246132718Skan	*pos++ = WLAN_EID_MOBILITY_DOMAIN;
247132718Skan	*pos++ = sizeof(*mdie);
248132718Skan	mdie = (struct rsn_mdie *) pos;
249132718Skan	pos += sizeof(*mdie);
250132718Skan	os_memcpy(mdie->mobility_domain, sm->mobility_domain,
251132718Skan		  MOBILITY_DOMAIN_ID_LEN);
252132718Skan	mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
253132718Skan		sm->mdie_ft_capab;
254132718Skan
255132718Skan	/* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
256132718Skan	ftie_pos = pos;
257132718Skan	*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
258132718Skan	ftie_len = pos++;
259132718Skan	ftie = (struct rsn_ftie *) pos;
260132718Skan	pos += sizeof(*ftie);
261132718Skan	os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
262132718Skan	if (anonce)
263132718Skan		os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
264132718Skan	if (kck) {
265169689Skan		/* R1KH-ID sub-element in third FT message */
266132718Skan		*pos++ = FTIE_SUBELEM_R1KH_ID;
267132718Skan		*pos++ = FT_R1KH_ID_LEN;
268132718Skan		os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
269132718Skan		pos += FT_R1KH_ID_LEN;
270169689Skan	}
271132718Skan	/* R0KH-ID sub-element */
272132718Skan	*pos++ = FTIE_SUBELEM_R0KH_ID;
273132718Skan	*pos++ = sm->r0kh_id_len;
274169689Skan	os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
275132718Skan	pos += sm->r0kh_id_len;
276132718Skan	*ftie_len = pos - ftie_len - 1;
277132718Skan
278132718Skan	if (ric_ies) {
279132718Skan		/* RIC Request */
280132718Skan		os_memcpy(pos, ric_ies, ric_ies_len);
281132718Skan		pos += ric_ies_len;
282169689Skan	}
283132718Skan
284132718Skan	if (kck) {
285132718Skan		/*
286132718Skan		 * IEEE Std 802.11r-2008, 11A.8.4
287132718Skan		 * MIC shall be calculated over:
288132718Skan		 * non-AP STA MAC address
289132718Skan		 * Target AP MAC address
290132718Skan		 * Transaction seq number (5 for ReassocReq, 3 otherwise)
291132718Skan		 * RSN IE
292132718Skan		 * MDIE
293132718Skan		 * FTIE (with MIC field set to 0)
294132718Skan		 * RIC-Request (if present)
295132718Skan		 */
296132718Skan		/* Information element count */
297132718Skan		ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
298132718Skan							       ric_ies_len);
299132718Skan		if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5,
300132718Skan			       ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
301132718Skan			       ftie_pos, 2 + *ftie_len,
302132718Skan			       (u8 *) rsnie, 2 + rsnie->len, ric_ies,
303132718Skan			       ric_ies_len, ftie->mic) < 0) {
304132718Skan			wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
305132718Skan			os_free(buf);
306132718Skan			return NULL;
307132718Skan		}
308132718Skan	}
309132718Skan
310169689Skan	*len = pos - buf;
311169689Skan
312169689Skan	return buf;
313169689Skan}
314169689Skan
315169689Skan
316132718Skanstatic int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
317132718Skan{
318132718Skan	int keylen;
319132718Skan	enum wpa_alg alg;
320132718Skan	u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
321132718Skan
322132718Skan	wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
323132718Skan
324132718Skan	if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
325132718Skan		wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
326132718Skan			   sm->pairwise_cipher);
327132718Skan		return -1;
328132718Skan	}
329132718Skan
330132718Skan	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
331132718Skan	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
332132718Skan
333132718Skan	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
334132718Skan			   sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
335132718Skan		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
336169689Skan		return -1;
337169689Skan	}
338169689Skan
339132718Skan	return 0;
340132718Skan}
341132718Skan
342132718Skan
343132718Skan/**
344132718Skan * wpa_ft_prepare_auth_request - Generate over-the-air auth request
345132718Skan * @sm: Pointer to WPA state machine data from wpa_sm_init()
346132718Skan * @mdie: Target AP MDIE
347132718Skan * Returns: 0 on success, -1 on failure
348169689Skan */
349132718Skanint wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
350132718Skan{
351132718Skan	u8 *ft_ies;
352132718Skan	size_t ft_ies_len;
353132718Skan
354132718Skan	/* Generate a new SNonce */
355132718Skan	if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
356169689Skan		wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
357132718Skan		return -1;
358132718Skan	}
359132718Skan
360132718Skan	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
361132718Skan				    NULL, sm->bssid, NULL, 0, mdie);
362132718Skan	if (ft_ies) {
363132718Skan		wpa_sm_update_ft_ies(sm, sm->mobility_domain,
364169689Skan				     ft_ies, ft_ies_len);
365132718Skan		os_free(ft_ies);
366132718Skan	}
367132718Skan
368132718Skan	return 0;
369132718Skan}
370132718Skan
371132718Skan
372132718Skanint wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
373132718Skan			    int ft_action, const u8 *target_ap,
374132718Skan			    const u8 *ric_ies, size_t ric_ies_len)
375132718Skan{
376132718Skan	u8 *ft_ies;
377132718Skan	size_t ft_ies_len, ptk_len;
378132718Skan	struct wpa_ft_ies parse;
379132718Skan	struct rsn_mdie *mdie;
380132718Skan	struct rsn_ftie *ftie;
381132718Skan	u8 ptk_name[WPA_PMK_NAME_LEN];
382132718Skan	int ret;
383132718Skan	const u8 *bssid;
384132718Skan
385132718Skan	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
386132718Skan	wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
387132718Skan
388132718Skan	if (ft_action) {
389169689Skan		if (!sm->over_the_ds_in_progress) {
390132718Skan			wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
391169689Skan				   "- drop FT Action Response");
392169689Skan			return -1;
393169689Skan		}
394132718Skan
395169689Skan		if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
396169689Skan			wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
397169689Skan				   "with this Target AP - drop FT Action "
398169689Skan				   "Response");
399169689Skan			return -1;
400169689Skan		}
401132718Skan	}
402169689Skan
403169689Skan	if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
404132718Skan	    sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
405132718Skan		wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
406132718Skan			   "enabled for this connection");
407132718Skan		return -1;
408132718Skan	}
409132718Skan
410132718Skan	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
411169689Skan		wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
412132718Skan		return -1;
413169689Skan	}
414169689Skan
415132718Skan	mdie = (struct rsn_mdie *) parse.mdie;
416169689Skan	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
417132718Skan	    os_memcmp(mdie->mobility_domain, sm->mobility_domain,
418169689Skan		      MOBILITY_DOMAIN_ID_LEN) != 0) {
419132718Skan		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
420132718Skan		return -1;
421169689Skan	}
422169689Skan
423169689Skan	ftie = (struct rsn_ftie *) parse.ftie;
424132718Skan	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
425132718Skan		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
426132718Skan		return -1;
427132718Skan	}
428132718Skan
429132718Skan	if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
430132718Skan		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
431132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
432132718Skan			    ftie->snonce, WPA_NONCE_LEN);
433259405Spfg		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
434132718Skan			    sm->snonce, WPA_NONCE_LEN);
435132718Skan		return -1;
436259563Spfg	}
437132718Skan
438132718Skan	if (parse.r0kh_id == NULL) {
439259405Spfg		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
440259405Spfg		return -1;
441259405Spfg	}
442259405Spfg
443132718Skan	if (parse.r0kh_id_len != sm->r0kh_id_len ||
444259405Spfg	    os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
445259405Spfg		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
446132718Skan			   "the current R0KH-ID");
447259405Spfg		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
448259405Spfg			    parse.r0kh_id, parse.r0kh_id_len);
449259405Spfg		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
450259405Spfg			    sm->r0kh_id, sm->r0kh_id_len);
451169689Skan		return -1;
452259405Spfg	}
453259405Spfg
454259405Spfg	if (parse.r1kh_id == NULL) {
455259405Spfg		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
456259405Spfg		return -1;
457259405Spfg	}
458259405Spfg
459259405Spfg	if (parse.rsn_pmkid == NULL ||
460259405Spfg	    os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) {
461259405Spfg		wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
462259405Spfg			   "RSNIE");
463259405Spfg		return -1;
464259405Spfg	}
465259405Spfg
466259405Spfg	os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
467259405Spfg	wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
468259405Spfg	wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
469259405Spfg	wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
470259405Spfg	os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
471259405Spfg	wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
472259405Spfg			  sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
473259405Spfg	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
474259405Spfg	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
475259405Spfg		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
476259405Spfg
477259405Spfg	bssid = target_ap;
478259405Spfg	ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
479259405Spfg	wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
480259405Spfg			  bssid, sm->pmk_r1_name,
481259405Spfg			  (u8 *) &sm->ptk, ptk_len, ptk_name);
482259405Spfg	wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
483259405Spfg			(u8 *) &sm->ptk, ptk_len);
484259405Spfg	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
485259405Spfg
486259405Spfg	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
487259405Spfg				    sm->pmk_r1_name, sm->ptk.kck, bssid,
488259405Spfg				    ric_ies, ric_ies_len,
489259405Spfg				    parse.mdie ? parse.mdie - 2 : NULL);
490259405Spfg	if (ft_ies) {
491259405Spfg		wpa_sm_update_ft_ies(sm, sm->mobility_domain,
492259405Spfg				     ft_ies, ft_ies_len);
493259405Spfg		os_free(ft_ies);
494259405Spfg	}
495132718Skan
496132718Skan	wpa_sm_mark_authenticated(sm, bssid);
497259405Spfg	ret = wpa_ft_install_ptk(sm, bssid);
498259405Spfg	if (ret) {
499132718Skan		/*
500259405Spfg		 * Some drivers do not support key configuration when we are
501132718Skan		 * not associated with the target AP. Work around this by
502132718Skan		 * trying again after the following reassociation gets
503132718Skan		 * completed.
504132718Skan		 */
505132718Skan		wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
506132718Skan			   "association - try again after reassociation");
507132718Skan		sm->set_ptk_after_assoc = 1;
508132718Skan	} else
509132718Skan		sm->set_ptk_after_assoc = 0;
510169689Skan
511169689Skan	sm->ft_completed = 1;
512169689Skan	if (ft_action) {
513132718Skan		/*
514169689Skan		 * The caller is expected trigger re-association with the
515132718Skan		 * Target AP.
516132718Skan		 */
517132718Skan		os_memcpy(sm->bssid, target_ap, ETH_ALEN);
518132718Skan	}
519132718Skan
520132718Skan	return 0;
521132718Skan}
522132718Skan
523132718Skan
524132718Skanint wpa_ft_is_completed(struct wpa_sm *sm)
525132718Skan{
526132718Skan	if (sm == NULL)
527132718Skan		return 0;
528132718Skan
529132718Skan	if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
530132718Skan	    sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
531132718Skan		return 0;
532132718Skan
533132718Skan	return sm->ft_completed;
534169689Skan}
535169689Skan
536132718Skan
537132718Skanstatic int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
538132718Skan				      size_t gtk_elem_len)
539132718Skan{
540132718Skan	u8 gtk[32];
541132718Skan	int keyidx;
542132718Skan	enum wpa_alg alg;
543132718Skan	size_t gtk_len, keylen, rsc_len;
544132718Skan
545132718Skan	if (gtk_elem == NULL) {
546132718Skan		wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
547132718Skan		return 0;
548132718Skan	}
549132718Skan
550132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
551132718Skan			gtk_elem, gtk_elem_len);
552132718Skan
553132718Skan	if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
554132718Skan	    gtk_elem_len - 19 > sizeof(gtk)) {
555132718Skan		wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
556132718Skan			   "length %lu", (unsigned long) gtk_elem_len);
557169689Skan		return -1;
558169689Skan	}
559132718Skan	gtk_len = gtk_elem_len - 19;
560132718Skan	if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) {
561132718Skan		wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
562132718Skan			   "decrypt GTK");
563132718Skan		return -1;
564132718Skan	}
565132718Skan
566132718Skan	keylen = wpa_cipher_key_len(sm->group_cipher);
567132718Skan	rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
568132718Skan	alg = wpa_cipher_to_alg(sm->group_cipher);
569132718Skan	if (alg == WPA_ALG_NONE) {
570132718Skan		wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
571132718Skan			   sm->group_cipher);
572132718Skan		return -1;
573132718Skan	}
574132718Skan
575132718Skan	if (gtk_len < keylen) {
576169689Skan		wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
577132718Skan		return -1;
578132718Skan	}
579132718Skan
580132718Skan	/* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
581132718Skan
582132718Skan	keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
583132718Skan
584169689Skan	if (gtk_elem[2] != keylen) {
585132718Skan		wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
586132718Skan			   "negotiated %lu",
587132718Skan			   gtk_elem[2], (unsigned long) keylen);
588132718Skan		return -1;
589132718Skan	}
590132718Skan
591132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
592132718Skan	if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
593132718Skan			   gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
594132718Skan		wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
595132718Skan			   "driver.");
596132718Skan		return -1;
597132718Skan	}
598132718Skan
599132718Skan	return 0;
600132718Skan}
601132718Skan
602132718Skan
603132718Skan#ifdef CONFIG_IEEE80211W
604132718Skanstatic int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
605132718Skan				       size_t igtk_elem_len)
606132718Skan{
607132718Skan	u8 igtk[WPA_IGTK_LEN];
608132718Skan	u16 keyidx;
609169689Skan
610132718Skan	if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
611132718Skan		return 0;
612132718Skan
613132718Skan	if (igtk_elem == NULL) {
614169689Skan		wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE");
615132718Skan		return 0;
616132718Skan	}
617169689Skan
618132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
619132718Skan			igtk_elem, igtk_elem_len);
620132718Skan
621169689Skan	if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
622169689Skan		wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
623169689Skan			   "length %lu", (unsigned long) igtk_elem_len);
624132718Skan		return -1;
625132718Skan	}
626132718Skan	if (igtk_elem[8] != WPA_IGTK_LEN) {
627132718Skan		wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
628132718Skan			   "%d", igtk_elem[8]);
629132718Skan		return -1;
630132718Skan	}
631132718Skan
632132718Skan	if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) {
633132718Skan		wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
634132718Skan			   "decrypt IGTK");
635132718Skan		return -1;
636132718Skan	}
637132718Skan
638132718Skan	/* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
639132718Skan
640132718Skan	keyidx = WPA_GET_LE16(igtk_elem);
641132718Skan
642132718Skan	wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
643132718Skan			WPA_IGTK_LEN);
644132718Skan	if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
645132718Skan			   igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
646132718Skan		wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
647132718Skan			   "driver.");
648169689Skan		return -1;
649169689Skan	}
650132718Skan
651132718Skan	return 0;
652132718Skan}
653169689Skan#endif /* CONFIG_IEEE80211W */
654169689Skan
655132718Skan
656132718Skanint wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
657132718Skan				 size_t ies_len, const u8 *src_addr)
658132718Skan{
659132718Skan	struct wpa_ft_ies parse;
660132718Skan	struct rsn_mdie *mdie;
661169689Skan	struct rsn_ftie *ftie;
662169689Skan	unsigned int count;
663132718Skan	u8 mic[16];
664132718Skan
665132718Skan	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
666132718Skan
667169689Skan	if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
668169689Skan	    sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
669169689Skan		wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
670132718Skan			   "enabled for this connection");
671132718Skan		return -1;
672169689Skan	}
673169689Skan
674132718Skan	if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
675132718Skan		wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
676132718Skan		return -1;
677132718Skan	}
678132718Skan
679132718Skan	mdie = (struct rsn_mdie *) parse.mdie;
680132718Skan	if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
681132718Skan	    os_memcmp(mdie->mobility_domain, sm->mobility_domain,
682132718Skan		      MOBILITY_DOMAIN_ID_LEN) != 0) {
683169689Skan		wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
684132718Skan		return -1;
685169689Skan	}
686132718Skan
687132718Skan	ftie = (struct rsn_ftie *) parse.ftie;
688132718Skan	if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
689169689Skan		wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
690132718Skan		return -1;
691132718Skan	}
692132718Skan
693132718Skan	if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
694132718Skan		wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
695132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
696132718Skan			    ftie->snonce, WPA_NONCE_LEN);
697132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
698132718Skan			    sm->snonce, WPA_NONCE_LEN);
699132718Skan		return -1;
700132718Skan	}
701169689Skan
702132718Skan	if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
703132718Skan		wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
704132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
705132718Skan			    ftie->anonce, WPA_NONCE_LEN);
706132718Skan		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
707132718Skan			    sm->anonce, WPA_NONCE_LEN);
708132718Skan		return -1;
709132718Skan	}
710132718Skan
711132718Skan	if (parse.r0kh_id == NULL) {
712132718Skan		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
713132718Skan		return -1;
714132718Skan	}
715132718Skan
716132718Skan	if (parse.r0kh_id_len != sm->r0kh_id_len ||
717132718Skan	    os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
718132718Skan		wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
719132718Skan			   "the current R0KH-ID");
720132718Skan		wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
721132718Skan			    parse.r0kh_id, parse.r0kh_id_len);
722132718Skan		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
723132718Skan			    sm->r0kh_id, sm->r0kh_id_len);
724132718Skan		return -1;
725132718Skan	}
726169689Skan
727169689Skan	if (parse.r1kh_id == NULL) {
728132718Skan		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
729132718Skan		return -1;
730132718Skan	}
731132718Skan
732132718Skan	if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
733169689Skan		wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
734132718Skan			   "ReassocResp");
735169689Skan		return -1;
736169689Skan	}
737169689Skan
738132718Skan	if (parse.rsn_pmkid == NULL ||
739132718Skan	    os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
740132718Skan		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
741169689Skan			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
742169689Skan		return -1;
743169689Skan	}
744169689Skan
745132718Skan	count = 3;
746132718Skan	if (parse.ric)
747169689Skan		count += ieee802_11_ie_count(parse.ric, parse.ric_len);
748169689Skan	if (ftie->mic_control[1] != count) {
749132718Skan		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
750132718Skan			   "Control: received %u expected %u",
751132718Skan			   ftie->mic_control[1], count);
752132718Skan		return -1;
753132718Skan	}
754132718Skan
755132718Skan	if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6,
756132718Skan		       parse.mdie - 2, parse.mdie_len + 2,
757132718Skan		       parse.ftie - 2, parse.ftie_len + 2,
758132718Skan		       parse.rsn - 2, parse.rsn_len + 2,
759132718Skan		       parse.ric, parse.ric_len,
760132718Skan		       mic) < 0) {
761132718Skan		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
762132718Skan		return -1;
763132718Skan	}
764132718Skan
765132718Skan	if (os_memcmp(mic, ftie->mic, 16) != 0) {
766169689Skan		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
767169689Skan		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
768132718Skan		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
769132718Skan		return -1;
770132718Skan	}
771132718Skan
772132718Skan	if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
773132718Skan		return -1;
774132718Skan
775132718Skan#ifdef CONFIG_IEEE80211W
776132718Skan	if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
777132718Skan		return -1;
778132718Skan#endif /* CONFIG_IEEE80211W */
779132718Skan
780132718Skan	if (sm->set_ptk_after_assoc) {
781132718Skan		wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
782132718Skan			   "are associated");
783132718Skan		if (wpa_ft_install_ptk(sm, src_addr) < 0)
784132718Skan			return -1;
785132718Skan		sm->set_ptk_after_assoc = 0;
786132718Skan	}
787132718Skan
788132718Skan	if (parse.ric) {
789132718Skan		wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
790132718Skan			    parse.ric, parse.ric_len);
791132718Skan		/* TODO: parse response and inform driver about results */
792132718Skan	}
793132718Skan
794132718Skan	return 0;
795132718Skan}
796132718Skan
797169689Skan
798132718Skan/**
799132718Skan * wpa_ft_start_over_ds - Generate over-the-DS auth request
800132718Skan * @sm: Pointer to WPA state machine data from wpa_sm_init()
801169689Skan * @target_ap: Target AP Address
802132718Skan * @mdie: Mobility Domain IE from the target AP
803132718Skan * Returns: 0 on success, -1 on failure
804169689Skan */
805132718Skanint wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
806132718Skan			 const u8 *mdie)
807132718Skan{
808132718Skan	u8 *ft_ies;
809132718Skan	size_t ft_ies_len;
810132718Skan
811132718Skan	wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
812132718Skan		   MAC2STR(target_ap));
813132718Skan
814169689Skan	/* Generate a new SNonce */
815132718Skan	if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
816132718Skan		wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
817169689Skan		return -1;
818132718Skan	}
819132718Skan
820132718Skan	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
821132718Skan				    NULL, target_ap, NULL, 0, mdie);
822132718Skan	if (ft_ies) {
823132718Skan		sm->over_the_ds_in_progress = 1;
824132718Skan		os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
825132718Skan		wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
826132718Skan		os_free(ft_ies);
827132718Skan	}
828132718Skan
829132718Skan	return 0;
830132718Skan}
831132718Skan
832132718Skan#endif /* CONFIG_IEEE80211R */
833132718Skan