xref: /freebsd/contrib/wpa/src/common/wpa_common.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * WPA/RSN - Shared functions for supplicant and authenticator
3*5b9c547cSRui Paulo  * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
12e28a4053SRui Paulo #include "crypto/md5.h"
13e28a4053SRui Paulo #include "crypto/sha1.h"
14e28a4053SRui Paulo #include "crypto/sha256.h"
15*5b9c547cSRui Paulo #include "crypto/sha384.h"
16e28a4053SRui Paulo #include "crypto/aes_wrap.h"
17e28a4053SRui Paulo #include "crypto/crypto.h"
1839beb93cSSam Leffler #include "ieee802_11_defs.h"
1939beb93cSSam Leffler #include "defs.h"
2039beb93cSSam Leffler #include "wpa_common.h"
2139beb93cSSam Leffler 
2239beb93cSSam Leffler 
23*5b9c547cSRui Paulo static unsigned int wpa_kck_len(int akmp)
24*5b9c547cSRui Paulo {
25*5b9c547cSRui Paulo 	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
26*5b9c547cSRui Paulo 		return 24;
27*5b9c547cSRui Paulo 	return 16;
28*5b9c547cSRui Paulo }
29*5b9c547cSRui Paulo 
30*5b9c547cSRui Paulo 
31*5b9c547cSRui Paulo static unsigned int wpa_kek_len(int akmp)
32*5b9c547cSRui Paulo {
33*5b9c547cSRui Paulo 	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
34*5b9c547cSRui Paulo 		return 32;
35*5b9c547cSRui Paulo 	return 16;
36*5b9c547cSRui Paulo }
37*5b9c547cSRui Paulo 
38*5b9c547cSRui Paulo 
39*5b9c547cSRui Paulo unsigned int wpa_mic_len(int akmp)
40*5b9c547cSRui Paulo {
41*5b9c547cSRui Paulo 	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
42*5b9c547cSRui Paulo 		return 24;
43*5b9c547cSRui Paulo 	return 16;
44*5b9c547cSRui Paulo }
45*5b9c547cSRui Paulo 
46*5b9c547cSRui Paulo 
4739beb93cSSam Leffler /**
4839beb93cSSam Leffler  * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
4939beb93cSSam Leffler  * @key: EAPOL-Key Key Confirmation Key (KCK)
50*5b9c547cSRui Paulo  * @key_len: KCK length in octets
51*5b9c547cSRui Paulo  * @akmp: WPA_KEY_MGMT_* used in key derivation
5239beb93cSSam Leffler  * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
5339beb93cSSam Leffler  * @buf: Pointer to the beginning of the EAPOL header (version field)
5439beb93cSSam Leffler  * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
5539beb93cSSam Leffler  * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
5639beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
5739beb93cSSam Leffler  *
5839beb93cSSam Leffler  * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
5939beb93cSSam Leffler  * to be cleared (all zeroes) when calling this function.
6039beb93cSSam Leffler  *
6139beb93cSSam Leffler  * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
6239beb93cSSam Leffler  * description of the Key MIC calculation. It includes packet data from the
6339beb93cSSam Leffler  * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
6439beb93cSSam Leffler  * happened during final editing of the standard and the correct behavior is
6539beb93cSSam Leffler  * defined in the last draft (IEEE 802.11i/D10).
6639beb93cSSam Leffler  */
67*5b9c547cSRui Paulo int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
68*5b9c547cSRui Paulo 		      const u8 *buf, size_t len, u8 *mic)
6939beb93cSSam Leffler {
70*5b9c547cSRui Paulo 	u8 hash[SHA384_MAC_LEN];
7139beb93cSSam Leffler 
7239beb93cSSam Leffler 	switch (ver) {
73f05cddf9SRui Paulo #ifndef CONFIG_FIPS
7439beb93cSSam Leffler 	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
75*5b9c547cSRui Paulo 		return hmac_md5(key, key_len, buf, len, mic);
76f05cddf9SRui Paulo #endif /* CONFIG_FIPS */
7739beb93cSSam Leffler 	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
78*5b9c547cSRui Paulo 		if (hmac_sha1(key, key_len, buf, len, hash))
79e28a4053SRui Paulo 			return -1;
8039beb93cSSam Leffler 		os_memcpy(mic, hash, MD5_MAC_LEN);
8139beb93cSSam Leffler 		break;
823157ba21SRui Paulo #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
8339beb93cSSam Leffler 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
8439beb93cSSam Leffler 		return omac1_aes_128(key, buf, len, mic);
853157ba21SRui Paulo #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
86*5b9c547cSRui Paulo 	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
87*5b9c547cSRui Paulo 		switch (akmp) {
88*5b9c547cSRui Paulo #ifdef CONFIG_HS20
89*5b9c547cSRui Paulo 		case WPA_KEY_MGMT_OSEN:
90*5b9c547cSRui Paulo 			return omac1_aes_128(key, buf, len, mic);
91*5b9c547cSRui Paulo #endif /* CONFIG_HS20 */
92*5b9c547cSRui Paulo #ifdef CONFIG_SUITEB
93*5b9c547cSRui Paulo 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
94*5b9c547cSRui Paulo 			if (hmac_sha256(key, key_len, buf, len, hash))
95*5b9c547cSRui Paulo 				return -1;
96*5b9c547cSRui Paulo 			os_memcpy(mic, hash, MD5_MAC_LEN);
97*5b9c547cSRui Paulo 			break;
98*5b9c547cSRui Paulo #endif /* CONFIG_SUITEB */
99*5b9c547cSRui Paulo #ifdef CONFIG_SUITEB192
100*5b9c547cSRui Paulo 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
101*5b9c547cSRui Paulo 			if (hmac_sha384(key, key_len, buf, len, hash))
102*5b9c547cSRui Paulo 				return -1;
103*5b9c547cSRui Paulo 			os_memcpy(mic, hash, 24);
104*5b9c547cSRui Paulo 			break;
105*5b9c547cSRui Paulo #endif /* CONFIG_SUITEB192 */
106*5b9c547cSRui Paulo 		default:
107*5b9c547cSRui Paulo 			return -1;
108*5b9c547cSRui Paulo 		}
109*5b9c547cSRui Paulo 		break;
11039beb93cSSam Leffler 	default:
11139beb93cSSam Leffler 		return -1;
11239beb93cSSam Leffler 	}
11339beb93cSSam Leffler 
11439beb93cSSam Leffler 	return 0;
11539beb93cSSam Leffler }
11639beb93cSSam Leffler 
11739beb93cSSam Leffler 
11839beb93cSSam Leffler /**
11939beb93cSSam Leffler  * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
12039beb93cSSam Leffler  * @pmk: Pairwise master key
12139beb93cSSam Leffler  * @pmk_len: Length of PMK
12239beb93cSSam Leffler  * @label: Label to use in derivation
12339beb93cSSam Leffler  * @addr1: AA or SA
12439beb93cSSam Leffler  * @addr2: SA or AA
12539beb93cSSam Leffler  * @nonce1: ANonce or SNonce
12639beb93cSSam Leffler  * @nonce2: SNonce or ANonce
12739beb93cSSam Leffler  * @ptk: Buffer for pairwise transient key
128*5b9c547cSRui Paulo  * @akmp: Negotiated AKM
129*5b9c547cSRui Paulo  * @cipher: Negotiated pairwise cipher
130*5b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure
13139beb93cSSam Leffler  *
13239beb93cSSam Leffler  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
13339beb93cSSam Leffler  * PTK = PRF-X(PMK, "Pairwise key expansion",
13439beb93cSSam Leffler  *             Min(AA, SA) || Max(AA, SA) ||
13539beb93cSSam Leffler  *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
13639beb93cSSam Leffler  *
13739beb93cSSam Leffler  * STK = PRF-X(SMK, "Peer key expansion",
13839beb93cSSam Leffler  *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
13939beb93cSSam Leffler  *             Min(INonce, PNonce) || Max(INonce, PNonce))
14039beb93cSSam Leffler  */
141*5b9c547cSRui Paulo int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
14239beb93cSSam Leffler 		   const u8 *addr1, const u8 *addr2,
14339beb93cSSam Leffler 		   const u8 *nonce1, const u8 *nonce2,
144*5b9c547cSRui Paulo 		   struct wpa_ptk *ptk, int akmp, int cipher)
14539beb93cSSam Leffler {
14639beb93cSSam Leffler 	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
147*5b9c547cSRui Paulo 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
148*5b9c547cSRui Paulo 	size_t ptk_len;
14939beb93cSSam Leffler 
15039beb93cSSam Leffler 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
15139beb93cSSam Leffler 		os_memcpy(data, addr1, ETH_ALEN);
15239beb93cSSam Leffler 		os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
15339beb93cSSam Leffler 	} else {
15439beb93cSSam Leffler 		os_memcpy(data, addr2, ETH_ALEN);
15539beb93cSSam Leffler 		os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
15639beb93cSSam Leffler 	}
15739beb93cSSam Leffler 
15839beb93cSSam Leffler 	if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
15939beb93cSSam Leffler 		os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
16039beb93cSSam Leffler 		os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
16139beb93cSSam Leffler 			  WPA_NONCE_LEN);
16239beb93cSSam Leffler 	} else {
16339beb93cSSam Leffler 		os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
16439beb93cSSam Leffler 		os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
16539beb93cSSam Leffler 			  WPA_NONCE_LEN);
16639beb93cSSam Leffler 	}
16739beb93cSSam Leffler 
168*5b9c547cSRui Paulo 	ptk->kck_len = wpa_kck_len(akmp);
169*5b9c547cSRui Paulo 	ptk->kek_len = wpa_kek_len(akmp);
170*5b9c547cSRui Paulo 	ptk->tk_len = wpa_cipher_key_len(cipher);
171*5b9c547cSRui Paulo 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
172*5b9c547cSRui Paulo 
17339beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
174*5b9c547cSRui Paulo 	if (wpa_key_mgmt_sha256(akmp))
17539beb93cSSam Leffler 		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
176*5b9c547cSRui Paulo 			   tmp, ptk_len);
17739beb93cSSam Leffler 	else
17839beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
179*5b9c547cSRui Paulo 		sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
18039beb93cSSam Leffler 
18139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
18239beb93cSSam Leffler 		   MAC2STR(addr1), MAC2STR(addr2));
183f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
184f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
18539beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
186*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
187*5b9c547cSRui Paulo 
188*5b9c547cSRui Paulo 	os_memcpy(ptk->kck, tmp, ptk->kck_len);
189*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
190*5b9c547cSRui Paulo 
191*5b9c547cSRui Paulo 	os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
192*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
193*5b9c547cSRui Paulo 
194*5b9c547cSRui Paulo 	os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
195*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
196*5b9c547cSRui Paulo 
197*5b9c547cSRui Paulo 	os_memset(tmp, 0, sizeof(tmp));
198*5b9c547cSRui Paulo 	return 0;
19939beb93cSSam Leffler }
20039beb93cSSam Leffler 
20139beb93cSSam Leffler 
20239beb93cSSam Leffler #ifdef CONFIG_IEEE80211R
203*5b9c547cSRui Paulo int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
204*5b9c547cSRui Paulo 	       const u8 *ap_addr, u8 transaction_seqnum,
205*5b9c547cSRui Paulo 	       const u8 *mdie, size_t mdie_len,
20639beb93cSSam Leffler 	       const u8 *ftie, size_t ftie_len,
20739beb93cSSam Leffler 	       const u8 *rsnie, size_t rsnie_len,
20839beb93cSSam Leffler 	       const u8 *ric, size_t ric_len, u8 *mic)
20939beb93cSSam Leffler {
21039beb93cSSam Leffler 	u8 *buf, *pos;
21139beb93cSSam Leffler 	size_t buf_len;
21239beb93cSSam Leffler 
213*5b9c547cSRui Paulo 	if (kck_len != 16) {
214*5b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
215*5b9c547cSRui Paulo 			   (unsigned int) kck_len);
216*5b9c547cSRui Paulo 		return -1;
217*5b9c547cSRui Paulo 	}
218*5b9c547cSRui Paulo 
21939beb93cSSam Leffler 	buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len;
22039beb93cSSam Leffler 	buf = os_malloc(buf_len);
22139beb93cSSam Leffler 	if (buf == NULL)
22239beb93cSSam Leffler 		return -1;
22339beb93cSSam Leffler 
22439beb93cSSam Leffler 	pos = buf;
22539beb93cSSam Leffler 	os_memcpy(pos, sta_addr, ETH_ALEN);
22639beb93cSSam Leffler 	pos += ETH_ALEN;
22739beb93cSSam Leffler 	os_memcpy(pos, ap_addr, ETH_ALEN);
22839beb93cSSam Leffler 	pos += ETH_ALEN;
22939beb93cSSam Leffler 	*pos++ = transaction_seqnum;
23039beb93cSSam Leffler 	if (rsnie) {
23139beb93cSSam Leffler 		os_memcpy(pos, rsnie, rsnie_len);
23239beb93cSSam Leffler 		pos += rsnie_len;
23339beb93cSSam Leffler 	}
23439beb93cSSam Leffler 	if (mdie) {
23539beb93cSSam Leffler 		os_memcpy(pos, mdie, mdie_len);
23639beb93cSSam Leffler 		pos += mdie_len;
23739beb93cSSam Leffler 	}
23839beb93cSSam Leffler 	if (ftie) {
23939beb93cSSam Leffler 		struct rsn_ftie *_ftie;
24039beb93cSSam Leffler 		os_memcpy(pos, ftie, ftie_len);
24139beb93cSSam Leffler 		if (ftie_len < 2 + sizeof(*_ftie)) {
24239beb93cSSam Leffler 			os_free(buf);
24339beb93cSSam Leffler 			return -1;
24439beb93cSSam Leffler 		}
24539beb93cSSam Leffler 		_ftie = (struct rsn_ftie *) (pos + 2);
24639beb93cSSam Leffler 		os_memset(_ftie->mic, 0, sizeof(_ftie->mic));
24739beb93cSSam Leffler 		pos += ftie_len;
24839beb93cSSam Leffler 	}
24939beb93cSSam Leffler 	if (ric) {
25039beb93cSSam Leffler 		os_memcpy(pos, ric, ric_len);
25139beb93cSSam Leffler 		pos += ric_len;
25239beb93cSSam Leffler 	}
25339beb93cSSam Leffler 
25439beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf);
25539beb93cSSam Leffler 	if (omac1_aes_128(kck, buf, pos - buf, mic)) {
25639beb93cSSam Leffler 		os_free(buf);
25739beb93cSSam Leffler 		return -1;
25839beb93cSSam Leffler 	}
25939beb93cSSam Leffler 
26039beb93cSSam Leffler 	os_free(buf);
26139beb93cSSam Leffler 
26239beb93cSSam Leffler 	return 0;
26339beb93cSSam Leffler }
264f05cddf9SRui Paulo 
265f05cddf9SRui Paulo 
266f05cddf9SRui Paulo static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
267f05cddf9SRui Paulo 			     struct wpa_ft_ies *parse)
268f05cddf9SRui Paulo {
269f05cddf9SRui Paulo 	const u8 *end, *pos;
270f05cddf9SRui Paulo 
271f05cddf9SRui Paulo 	parse->ftie = ie;
272f05cddf9SRui Paulo 	parse->ftie_len = ie_len;
273f05cddf9SRui Paulo 
274f05cddf9SRui Paulo 	pos = ie + sizeof(struct rsn_ftie);
275f05cddf9SRui Paulo 	end = ie + ie_len;
276f05cddf9SRui Paulo 
277f05cddf9SRui Paulo 	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
278f05cddf9SRui Paulo 		switch (pos[0]) {
279f05cddf9SRui Paulo 		case FTIE_SUBELEM_R1KH_ID:
280f05cddf9SRui Paulo 			if (pos[1] != FT_R1KH_ID_LEN) {
281f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
282f05cddf9SRui Paulo 					   "length in FTIE: %d", pos[1]);
283f05cddf9SRui Paulo 				return -1;
284f05cddf9SRui Paulo 			}
285f05cddf9SRui Paulo 			parse->r1kh_id = pos + 2;
286f05cddf9SRui Paulo 			break;
287f05cddf9SRui Paulo 		case FTIE_SUBELEM_GTK:
288f05cddf9SRui Paulo 			parse->gtk = pos + 2;
289f05cddf9SRui Paulo 			parse->gtk_len = pos[1];
290f05cddf9SRui Paulo 			break;
291f05cddf9SRui Paulo 		case FTIE_SUBELEM_R0KH_ID:
292f05cddf9SRui Paulo 			if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
293f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
294f05cddf9SRui Paulo 					   "length in FTIE: %d", pos[1]);
295f05cddf9SRui Paulo 				return -1;
296f05cddf9SRui Paulo 			}
297f05cddf9SRui Paulo 			parse->r0kh_id = pos + 2;
298f05cddf9SRui Paulo 			parse->r0kh_id_len = pos[1];
299f05cddf9SRui Paulo 			break;
300f05cddf9SRui Paulo #ifdef CONFIG_IEEE80211W
301f05cddf9SRui Paulo 		case FTIE_SUBELEM_IGTK:
302f05cddf9SRui Paulo 			parse->igtk = pos + 2;
303f05cddf9SRui Paulo 			parse->igtk_len = pos[1];
304f05cddf9SRui Paulo 			break;
305f05cddf9SRui Paulo #endif /* CONFIG_IEEE80211W */
306f05cddf9SRui Paulo 		}
307f05cddf9SRui Paulo 
308f05cddf9SRui Paulo 		pos += 2 + pos[1];
309f05cddf9SRui Paulo 	}
310f05cddf9SRui Paulo 
311f05cddf9SRui Paulo 	return 0;
312f05cddf9SRui Paulo }
313f05cddf9SRui Paulo 
314f05cddf9SRui Paulo 
315f05cddf9SRui Paulo int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
316f05cddf9SRui Paulo 		     struct wpa_ft_ies *parse)
317f05cddf9SRui Paulo {
318f05cddf9SRui Paulo 	const u8 *end, *pos;
319f05cddf9SRui Paulo 	struct wpa_ie_data data;
320f05cddf9SRui Paulo 	int ret;
321f05cddf9SRui Paulo 	const struct rsn_ftie *ftie;
322f05cddf9SRui Paulo 	int prot_ie_count = 0;
323f05cddf9SRui Paulo 
324f05cddf9SRui Paulo 	os_memset(parse, 0, sizeof(*parse));
325f05cddf9SRui Paulo 	if (ies == NULL)
326f05cddf9SRui Paulo 		return 0;
327f05cddf9SRui Paulo 
328f05cddf9SRui Paulo 	pos = ies;
329f05cddf9SRui Paulo 	end = ies + ies_len;
330f05cddf9SRui Paulo 	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
331f05cddf9SRui Paulo 		switch (pos[0]) {
332f05cddf9SRui Paulo 		case WLAN_EID_RSN:
333f05cddf9SRui Paulo 			parse->rsn = pos + 2;
334f05cddf9SRui Paulo 			parse->rsn_len = pos[1];
335f05cddf9SRui Paulo 			ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
336f05cddf9SRui Paulo 						   parse->rsn_len + 2,
337f05cddf9SRui Paulo 						   &data);
338f05cddf9SRui Paulo 			if (ret < 0) {
339f05cddf9SRui Paulo 				wpa_printf(MSG_DEBUG, "FT: Failed to parse "
340f05cddf9SRui Paulo 					   "RSN IE: %d", ret);
341f05cddf9SRui Paulo 				return -1;
342f05cddf9SRui Paulo 			}
343f05cddf9SRui Paulo 			if (data.num_pmkid == 1 && data.pmkid)
344f05cddf9SRui Paulo 				parse->rsn_pmkid = data.pmkid;
345f05cddf9SRui Paulo 			break;
346f05cddf9SRui Paulo 		case WLAN_EID_MOBILITY_DOMAIN:
347f05cddf9SRui Paulo 			parse->mdie = pos + 2;
348f05cddf9SRui Paulo 			parse->mdie_len = pos[1];
349f05cddf9SRui Paulo 			break;
350f05cddf9SRui Paulo 		case WLAN_EID_FAST_BSS_TRANSITION:
351f05cddf9SRui Paulo 			if (pos[1] < sizeof(*ftie))
352f05cddf9SRui Paulo 				return -1;
353f05cddf9SRui Paulo 			ftie = (const struct rsn_ftie *) (pos + 2);
354f05cddf9SRui Paulo 			prot_ie_count = ftie->mic_control[1];
355f05cddf9SRui Paulo 			if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
356f05cddf9SRui Paulo 				return -1;
357f05cddf9SRui Paulo 			break;
358f05cddf9SRui Paulo 		case WLAN_EID_TIMEOUT_INTERVAL:
359f05cddf9SRui Paulo 			parse->tie = pos + 2;
360f05cddf9SRui Paulo 			parse->tie_len = pos[1];
361f05cddf9SRui Paulo 			break;
362f05cddf9SRui Paulo 		case WLAN_EID_RIC_DATA:
363f05cddf9SRui Paulo 			if (parse->ric == NULL)
364f05cddf9SRui Paulo 				parse->ric = pos;
365f05cddf9SRui Paulo 			break;
366f05cddf9SRui Paulo 		}
367f05cddf9SRui Paulo 
368f05cddf9SRui Paulo 		pos += 2 + pos[1];
369f05cddf9SRui Paulo 	}
370f05cddf9SRui Paulo 
371f05cddf9SRui Paulo 	if (prot_ie_count == 0)
372f05cddf9SRui Paulo 		return 0; /* no MIC */
373f05cddf9SRui Paulo 
374f05cddf9SRui Paulo 	/*
375f05cddf9SRui Paulo 	 * Check that the protected IE count matches with IEs included in the
376f05cddf9SRui Paulo 	 * frame.
377f05cddf9SRui Paulo 	 */
378f05cddf9SRui Paulo 	if (parse->rsn)
379f05cddf9SRui Paulo 		prot_ie_count--;
380f05cddf9SRui Paulo 	if (parse->mdie)
381f05cddf9SRui Paulo 		prot_ie_count--;
382f05cddf9SRui Paulo 	if (parse->ftie)
383f05cddf9SRui Paulo 		prot_ie_count--;
384f05cddf9SRui Paulo 	if (prot_ie_count < 0) {
385f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
386f05cddf9SRui Paulo 			   "the protected IE count");
387f05cddf9SRui Paulo 		return -1;
388f05cddf9SRui Paulo 	}
389f05cddf9SRui Paulo 
390f05cddf9SRui Paulo 	if (prot_ie_count == 0 && parse->ric) {
391f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
392f05cddf9SRui Paulo 			   "included in protected IE count");
393f05cddf9SRui Paulo 		return -1;
394f05cddf9SRui Paulo 	}
395f05cddf9SRui Paulo 
396f05cddf9SRui Paulo 	/* Determine the end of the RIC IE(s) */
397f05cddf9SRui Paulo 	pos = parse->ric;
398f05cddf9SRui Paulo 	while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
399f05cddf9SRui Paulo 	       prot_ie_count) {
400f05cddf9SRui Paulo 		prot_ie_count--;
401f05cddf9SRui Paulo 		pos += 2 + pos[1];
402f05cddf9SRui Paulo 	}
403f05cddf9SRui Paulo 	parse->ric_len = pos - parse->ric;
404f05cddf9SRui Paulo 	if (prot_ie_count) {
405f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
406f05cddf9SRui Paulo 			   "frame", (int) prot_ie_count);
407f05cddf9SRui Paulo 		return -1;
408f05cddf9SRui Paulo 	}
409f05cddf9SRui Paulo 
410f05cddf9SRui Paulo 	return 0;
411f05cddf9SRui Paulo }
41239beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */
41339beb93cSSam Leffler 
41439beb93cSSam Leffler 
41539beb93cSSam Leffler static int rsn_selector_to_bitfield(const u8 *s)
41639beb93cSSam Leffler {
41739beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
41839beb93cSSam Leffler 		return WPA_CIPHER_NONE;
41939beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40)
42039beb93cSSam Leffler 		return WPA_CIPHER_WEP40;
42139beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
42239beb93cSSam Leffler 		return WPA_CIPHER_TKIP;
42339beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
42439beb93cSSam Leffler 		return WPA_CIPHER_CCMP;
42539beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104)
42639beb93cSSam Leffler 		return WPA_CIPHER_WEP104;
42739beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
42839beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
42939beb93cSSam Leffler 		return WPA_CIPHER_AES_128_CMAC;
43039beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
431f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
432f05cddf9SRui Paulo 		return WPA_CIPHER_GCMP;
433*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
434*5b9c547cSRui Paulo 		return WPA_CIPHER_CCMP_256;
435*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256)
436*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP_256;
437*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128)
438*5b9c547cSRui Paulo 		return WPA_CIPHER_BIP_GMAC_128;
439*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256)
440*5b9c547cSRui Paulo 		return WPA_CIPHER_BIP_GMAC_256;
441*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256)
442*5b9c547cSRui Paulo 		return WPA_CIPHER_BIP_CMAC_256;
443*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
444*5b9c547cSRui Paulo 		return WPA_CIPHER_GTK_NOT_USED;
44539beb93cSSam Leffler 	return 0;
44639beb93cSSam Leffler }
44739beb93cSSam Leffler 
44839beb93cSSam Leffler 
44939beb93cSSam Leffler static int rsn_key_mgmt_to_bitfield(const u8 *s)
45039beb93cSSam Leffler {
45139beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
45239beb93cSSam Leffler 		return WPA_KEY_MGMT_IEEE8021X;
45339beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
45439beb93cSSam Leffler 		return WPA_KEY_MGMT_PSK;
45539beb93cSSam Leffler #ifdef CONFIG_IEEE80211R
45639beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
45739beb93cSSam Leffler 		return WPA_KEY_MGMT_FT_IEEE8021X;
45839beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
45939beb93cSSam Leffler 		return WPA_KEY_MGMT_FT_PSK;
46039beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */
46139beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
46239beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
46339beb93cSSam Leffler 		return WPA_KEY_MGMT_IEEE8021X_SHA256;
46439beb93cSSam Leffler 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
46539beb93cSSam Leffler 		return WPA_KEY_MGMT_PSK_SHA256;
46639beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
467f05cddf9SRui Paulo #ifdef CONFIG_SAE
468f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
469f05cddf9SRui Paulo 		return WPA_KEY_MGMT_SAE;
470f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
471f05cddf9SRui Paulo 		return WPA_KEY_MGMT_FT_SAE;
472f05cddf9SRui Paulo #endif /* CONFIG_SAE */
473*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
474*5b9c547cSRui Paulo 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
475*5b9c547cSRui Paulo 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
476*5b9c547cSRui Paulo 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
47739beb93cSSam Leffler 	return 0;
47839beb93cSSam Leffler }
479*5b9c547cSRui Paulo 
480*5b9c547cSRui Paulo 
481*5b9c547cSRui Paulo static int wpa_cipher_valid_group(int cipher)
482*5b9c547cSRui Paulo {
483*5b9c547cSRui Paulo 	return wpa_cipher_valid_pairwise(cipher) ||
484*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_WEP104 ||
485*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_WEP40 ||
486*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_GTK_NOT_USED;
487*5b9c547cSRui Paulo }
488*5b9c547cSRui Paulo 
489*5b9c547cSRui Paulo 
490*5b9c547cSRui Paulo #ifdef CONFIG_IEEE80211W
491*5b9c547cSRui Paulo int wpa_cipher_valid_mgmt_group(int cipher)
492*5b9c547cSRui Paulo {
493*5b9c547cSRui Paulo 	return cipher == WPA_CIPHER_AES_128_CMAC ||
494*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_BIP_GMAC_128 ||
495*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_BIP_GMAC_256 ||
496*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_BIP_CMAC_256;
497*5b9c547cSRui Paulo }
498*5b9c547cSRui Paulo #endif /* CONFIG_IEEE80211W */
49939beb93cSSam Leffler 
50039beb93cSSam Leffler 
50139beb93cSSam Leffler /**
50239beb93cSSam Leffler  * wpa_parse_wpa_ie_rsn - Parse RSN IE
50339beb93cSSam Leffler  * @rsn_ie: Buffer containing RSN IE
50439beb93cSSam Leffler  * @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
50539beb93cSSam Leffler  * @data: Pointer to structure that will be filled in with parsed data
50639beb93cSSam Leffler  * Returns: 0 on success, <0 on failure
50739beb93cSSam Leffler  */
50839beb93cSSam Leffler int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
50939beb93cSSam Leffler 			 struct wpa_ie_data *data)
51039beb93cSSam Leffler {
51139beb93cSSam Leffler 	const struct rsn_ie_hdr *hdr;
51239beb93cSSam Leffler 	const u8 *pos;
51339beb93cSSam Leffler 	int left;
51439beb93cSSam Leffler 	int i, count;
51539beb93cSSam Leffler 
51639beb93cSSam Leffler 	os_memset(data, 0, sizeof(*data));
51739beb93cSSam Leffler 	data->proto = WPA_PROTO_RSN;
51839beb93cSSam Leffler 	data->pairwise_cipher = WPA_CIPHER_CCMP;
51939beb93cSSam Leffler 	data->group_cipher = WPA_CIPHER_CCMP;
52039beb93cSSam Leffler 	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
52139beb93cSSam Leffler 	data->capabilities = 0;
52239beb93cSSam Leffler 	data->pmkid = NULL;
52339beb93cSSam Leffler 	data->num_pmkid = 0;
52439beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
52539beb93cSSam Leffler 	data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
52639beb93cSSam Leffler #else /* CONFIG_IEEE80211W */
52739beb93cSSam Leffler 	data->mgmt_group_cipher = 0;
52839beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
52939beb93cSSam Leffler 
53039beb93cSSam Leffler 	if (rsn_ie_len == 0) {
53139beb93cSSam Leffler 		/* No RSN IE - fail silently */
53239beb93cSSam Leffler 		return -1;
53339beb93cSSam Leffler 	}
53439beb93cSSam Leffler 
53539beb93cSSam Leffler 	if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
53639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
53739beb93cSSam Leffler 			   __func__, (unsigned long) rsn_ie_len);
53839beb93cSSam Leffler 		return -1;
53939beb93cSSam Leffler 	}
54039beb93cSSam Leffler 
54139beb93cSSam Leffler 	hdr = (const struct rsn_ie_hdr *) rsn_ie;
54239beb93cSSam Leffler 
54339beb93cSSam Leffler 	if (hdr->elem_id != WLAN_EID_RSN ||
54439beb93cSSam Leffler 	    hdr->len != rsn_ie_len - 2 ||
54539beb93cSSam Leffler 	    WPA_GET_LE16(hdr->version) != RSN_VERSION) {
54639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
54739beb93cSSam Leffler 			   __func__);
54839beb93cSSam Leffler 		return -2;
54939beb93cSSam Leffler 	}
55039beb93cSSam Leffler 
55139beb93cSSam Leffler 	pos = (const u8 *) (hdr + 1);
55239beb93cSSam Leffler 	left = rsn_ie_len - sizeof(*hdr);
55339beb93cSSam Leffler 
55439beb93cSSam Leffler 	if (left >= RSN_SELECTOR_LEN) {
55539beb93cSSam Leffler 		data->group_cipher = rsn_selector_to_bitfield(pos);
556*5b9c547cSRui Paulo 		if (!wpa_cipher_valid_group(data->group_cipher)) {
557*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
558*5b9c547cSRui Paulo 				   __func__, data->group_cipher);
55939beb93cSSam Leffler 			return -1;
56039beb93cSSam Leffler 		}
56139beb93cSSam Leffler 		pos += RSN_SELECTOR_LEN;
56239beb93cSSam Leffler 		left -= RSN_SELECTOR_LEN;
56339beb93cSSam Leffler 	} else if (left > 0) {
56439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
56539beb93cSSam Leffler 			   __func__, left);
56639beb93cSSam Leffler 		return -3;
56739beb93cSSam Leffler 	}
56839beb93cSSam Leffler 
56939beb93cSSam Leffler 	if (left >= 2) {
57039beb93cSSam Leffler 		data->pairwise_cipher = 0;
57139beb93cSSam Leffler 		count = WPA_GET_LE16(pos);
57239beb93cSSam Leffler 		pos += 2;
57339beb93cSSam Leffler 		left -= 2;
574*5b9c547cSRui Paulo 		if (count == 0 || count > left / RSN_SELECTOR_LEN) {
57539beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
57639beb93cSSam Leffler 				   "count %u left %u", __func__, count, left);
57739beb93cSSam Leffler 			return -4;
57839beb93cSSam Leffler 		}
57939beb93cSSam Leffler 		for (i = 0; i < count; i++) {
58039beb93cSSam Leffler 			data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
58139beb93cSSam Leffler 			pos += RSN_SELECTOR_LEN;
58239beb93cSSam Leffler 			left -= RSN_SELECTOR_LEN;
58339beb93cSSam Leffler 		}
58439beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
58539beb93cSSam Leffler 		if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
58639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
58739beb93cSSam Leffler 				   "pairwise cipher", __func__);
58839beb93cSSam Leffler 			return -1;
58939beb93cSSam Leffler 		}
59039beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
59139beb93cSSam Leffler 	} else if (left == 1) {
59239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
59339beb93cSSam Leffler 			   __func__);
59439beb93cSSam Leffler 		return -5;
59539beb93cSSam Leffler 	}
59639beb93cSSam Leffler 
59739beb93cSSam Leffler 	if (left >= 2) {
59839beb93cSSam Leffler 		data->key_mgmt = 0;
59939beb93cSSam Leffler 		count = WPA_GET_LE16(pos);
60039beb93cSSam Leffler 		pos += 2;
60139beb93cSSam Leffler 		left -= 2;
602*5b9c547cSRui Paulo 		if (count == 0 || count > left / RSN_SELECTOR_LEN) {
60339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
60439beb93cSSam Leffler 				   "count %u left %u", __func__, count, left);
60539beb93cSSam Leffler 			return -6;
60639beb93cSSam Leffler 		}
60739beb93cSSam Leffler 		for (i = 0; i < count; i++) {
60839beb93cSSam Leffler 			data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
60939beb93cSSam Leffler 			pos += RSN_SELECTOR_LEN;
61039beb93cSSam Leffler 			left -= RSN_SELECTOR_LEN;
61139beb93cSSam Leffler 		}
61239beb93cSSam Leffler 	} else if (left == 1) {
61339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
61439beb93cSSam Leffler 			   __func__);
61539beb93cSSam Leffler 		return -7;
61639beb93cSSam Leffler 	}
61739beb93cSSam Leffler 
61839beb93cSSam Leffler 	if (left >= 2) {
61939beb93cSSam Leffler 		data->capabilities = WPA_GET_LE16(pos);
62039beb93cSSam Leffler 		pos += 2;
62139beb93cSSam Leffler 		left -= 2;
62239beb93cSSam Leffler 	}
62339beb93cSSam Leffler 
62439beb93cSSam Leffler 	if (left >= 2) {
625*5b9c547cSRui Paulo 		u16 num_pmkid = WPA_GET_LE16(pos);
62639beb93cSSam Leffler 		pos += 2;
62739beb93cSSam Leffler 		left -= 2;
628*5b9c547cSRui Paulo 		if (num_pmkid > (unsigned int) left / PMKID_LEN) {
62939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
630*5b9c547cSRui Paulo 				   "(num_pmkid=%u left=%d)",
631*5b9c547cSRui Paulo 				   __func__, num_pmkid, left);
63239beb93cSSam Leffler 			data->num_pmkid = 0;
63339beb93cSSam Leffler 			return -9;
63439beb93cSSam Leffler 		} else {
635*5b9c547cSRui Paulo 			data->num_pmkid = num_pmkid;
63639beb93cSSam Leffler 			data->pmkid = pos;
63739beb93cSSam Leffler 			pos += data->num_pmkid * PMKID_LEN;
63839beb93cSSam Leffler 			left -= data->num_pmkid * PMKID_LEN;
63939beb93cSSam Leffler 		}
64039beb93cSSam Leffler 	}
64139beb93cSSam Leffler 
64239beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
64339beb93cSSam Leffler 	if (left >= 4) {
64439beb93cSSam Leffler 		data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
645*5b9c547cSRui Paulo 		if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
64639beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "%s: Unsupported management "
64739beb93cSSam Leffler 				   "group cipher 0x%x", __func__,
64839beb93cSSam Leffler 				   data->mgmt_group_cipher);
64939beb93cSSam Leffler 			return -10;
65039beb93cSSam Leffler 		}
65139beb93cSSam Leffler 		pos += RSN_SELECTOR_LEN;
65239beb93cSSam Leffler 		left -= RSN_SELECTOR_LEN;
65339beb93cSSam Leffler 	}
65439beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
65539beb93cSSam Leffler 
65639beb93cSSam Leffler 	if (left > 0) {
657*5b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG,
658*5b9c547cSRui Paulo 			    "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
659*5b9c547cSRui Paulo 			    pos, left);
66039beb93cSSam Leffler 	}
66139beb93cSSam Leffler 
66239beb93cSSam Leffler 	return 0;
66339beb93cSSam Leffler }
66439beb93cSSam Leffler 
66539beb93cSSam Leffler 
666f05cddf9SRui Paulo static int wpa_selector_to_bitfield(const u8 *s)
667f05cddf9SRui Paulo {
668f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
669f05cddf9SRui Paulo 		return WPA_CIPHER_NONE;
670f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
671f05cddf9SRui Paulo 		return WPA_CIPHER_WEP40;
672f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
673f05cddf9SRui Paulo 		return WPA_CIPHER_TKIP;
674f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
675f05cddf9SRui Paulo 		return WPA_CIPHER_CCMP;
676f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
677f05cddf9SRui Paulo 		return WPA_CIPHER_WEP104;
678f05cddf9SRui Paulo 	return 0;
679f05cddf9SRui Paulo }
680f05cddf9SRui Paulo 
681f05cddf9SRui Paulo 
682f05cddf9SRui Paulo static int wpa_key_mgmt_to_bitfield(const u8 *s)
683f05cddf9SRui Paulo {
684f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
685f05cddf9SRui Paulo 		return WPA_KEY_MGMT_IEEE8021X;
686f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
687f05cddf9SRui Paulo 		return WPA_KEY_MGMT_PSK;
688f05cddf9SRui Paulo 	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
689f05cddf9SRui Paulo 		return WPA_KEY_MGMT_WPA_NONE;
690f05cddf9SRui Paulo 	return 0;
691f05cddf9SRui Paulo }
692f05cddf9SRui Paulo 
693f05cddf9SRui Paulo 
694f05cddf9SRui Paulo int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
695f05cddf9SRui Paulo 			 struct wpa_ie_data *data)
696f05cddf9SRui Paulo {
697f05cddf9SRui Paulo 	const struct wpa_ie_hdr *hdr;
698f05cddf9SRui Paulo 	const u8 *pos;
699f05cddf9SRui Paulo 	int left;
700f05cddf9SRui Paulo 	int i, count;
701f05cddf9SRui Paulo 
702f05cddf9SRui Paulo 	os_memset(data, 0, sizeof(*data));
703f05cddf9SRui Paulo 	data->proto = WPA_PROTO_WPA;
704f05cddf9SRui Paulo 	data->pairwise_cipher = WPA_CIPHER_TKIP;
705f05cddf9SRui Paulo 	data->group_cipher = WPA_CIPHER_TKIP;
706f05cddf9SRui Paulo 	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
707f05cddf9SRui Paulo 	data->capabilities = 0;
708f05cddf9SRui Paulo 	data->pmkid = NULL;
709f05cddf9SRui Paulo 	data->num_pmkid = 0;
710f05cddf9SRui Paulo 	data->mgmt_group_cipher = 0;
711f05cddf9SRui Paulo 
712f05cddf9SRui Paulo 	if (wpa_ie_len == 0) {
713f05cddf9SRui Paulo 		/* No WPA IE - fail silently */
714f05cddf9SRui Paulo 		return -1;
715f05cddf9SRui Paulo 	}
716f05cddf9SRui Paulo 
717f05cddf9SRui Paulo 	if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
718f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
719f05cddf9SRui Paulo 			   __func__, (unsigned long) wpa_ie_len);
720f05cddf9SRui Paulo 		return -1;
721f05cddf9SRui Paulo 	}
722f05cddf9SRui Paulo 
723f05cddf9SRui Paulo 	hdr = (const struct wpa_ie_hdr *) wpa_ie;
724f05cddf9SRui Paulo 
725f05cddf9SRui Paulo 	if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
726f05cddf9SRui Paulo 	    hdr->len != wpa_ie_len - 2 ||
727f05cddf9SRui Paulo 	    RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
728f05cddf9SRui Paulo 	    WPA_GET_LE16(hdr->version) != WPA_VERSION) {
729f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
730f05cddf9SRui Paulo 			   __func__);
731f05cddf9SRui Paulo 		return -2;
732f05cddf9SRui Paulo 	}
733f05cddf9SRui Paulo 
734f05cddf9SRui Paulo 	pos = (const u8 *) (hdr + 1);
735f05cddf9SRui Paulo 	left = wpa_ie_len - sizeof(*hdr);
736f05cddf9SRui Paulo 
737f05cddf9SRui Paulo 	if (left >= WPA_SELECTOR_LEN) {
738f05cddf9SRui Paulo 		data->group_cipher = wpa_selector_to_bitfield(pos);
739f05cddf9SRui Paulo 		pos += WPA_SELECTOR_LEN;
740f05cddf9SRui Paulo 		left -= WPA_SELECTOR_LEN;
741f05cddf9SRui Paulo 	} else if (left > 0) {
742f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
743f05cddf9SRui Paulo 			   __func__, left);
744f05cddf9SRui Paulo 		return -3;
745f05cddf9SRui Paulo 	}
746f05cddf9SRui Paulo 
747f05cddf9SRui Paulo 	if (left >= 2) {
748f05cddf9SRui Paulo 		data->pairwise_cipher = 0;
749f05cddf9SRui Paulo 		count = WPA_GET_LE16(pos);
750f05cddf9SRui Paulo 		pos += 2;
751f05cddf9SRui Paulo 		left -= 2;
752*5b9c547cSRui Paulo 		if (count == 0 || count > left / WPA_SELECTOR_LEN) {
753f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
754f05cddf9SRui Paulo 				   "count %u left %u", __func__, count, left);
755f05cddf9SRui Paulo 			return -4;
756f05cddf9SRui Paulo 		}
757f05cddf9SRui Paulo 		for (i = 0; i < count; i++) {
758f05cddf9SRui Paulo 			data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
759f05cddf9SRui Paulo 			pos += WPA_SELECTOR_LEN;
760f05cddf9SRui Paulo 			left -= WPA_SELECTOR_LEN;
761f05cddf9SRui Paulo 		}
762f05cddf9SRui Paulo 	} else if (left == 1) {
763f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
764f05cddf9SRui Paulo 			   __func__);
765f05cddf9SRui Paulo 		return -5;
766f05cddf9SRui Paulo 	}
767f05cddf9SRui Paulo 
768f05cddf9SRui Paulo 	if (left >= 2) {
769f05cddf9SRui Paulo 		data->key_mgmt = 0;
770f05cddf9SRui Paulo 		count = WPA_GET_LE16(pos);
771f05cddf9SRui Paulo 		pos += 2;
772f05cddf9SRui Paulo 		left -= 2;
773*5b9c547cSRui Paulo 		if (count == 0 || count > left / WPA_SELECTOR_LEN) {
774f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
775f05cddf9SRui Paulo 				   "count %u left %u", __func__, count, left);
776f05cddf9SRui Paulo 			return -6;
777f05cddf9SRui Paulo 		}
778f05cddf9SRui Paulo 		for (i = 0; i < count; i++) {
779f05cddf9SRui Paulo 			data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
780f05cddf9SRui Paulo 			pos += WPA_SELECTOR_LEN;
781f05cddf9SRui Paulo 			left -= WPA_SELECTOR_LEN;
782f05cddf9SRui Paulo 		}
783f05cddf9SRui Paulo 	} else if (left == 1) {
784f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
785f05cddf9SRui Paulo 			   __func__);
786f05cddf9SRui Paulo 		return -7;
787f05cddf9SRui Paulo 	}
788f05cddf9SRui Paulo 
789f05cddf9SRui Paulo 	if (left >= 2) {
790f05cddf9SRui Paulo 		data->capabilities = WPA_GET_LE16(pos);
791f05cddf9SRui Paulo 		pos += 2;
792f05cddf9SRui Paulo 		left -= 2;
793f05cddf9SRui Paulo 	}
794f05cddf9SRui Paulo 
795f05cddf9SRui Paulo 	if (left > 0) {
796*5b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG,
797*5b9c547cSRui Paulo 			    "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
798*5b9c547cSRui Paulo 			    pos, left);
799f05cddf9SRui Paulo 	}
800f05cddf9SRui Paulo 
801f05cddf9SRui Paulo 	return 0;
802f05cddf9SRui Paulo }
803f05cddf9SRui Paulo 
804f05cddf9SRui Paulo 
80539beb93cSSam Leffler #ifdef CONFIG_IEEE80211R
80639beb93cSSam Leffler 
80739beb93cSSam Leffler /**
80839beb93cSSam Leffler  * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name
80939beb93cSSam Leffler  *
81039beb93cSSam Leffler  * IEEE Std 802.11r-2008 - 8.5.1.5.3
81139beb93cSSam Leffler  */
81239beb93cSSam Leffler void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
81339beb93cSSam Leffler 		       const u8 *ssid, size_t ssid_len,
81439beb93cSSam Leffler 		       const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
81539beb93cSSam Leffler 		       const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
81639beb93cSSam Leffler {
81739beb93cSSam Leffler 	u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
81839beb93cSSam Leffler 	       FT_R0KH_ID_MAX_LEN + ETH_ALEN];
81939beb93cSSam Leffler 	u8 *pos, r0_key_data[48], hash[32];
82039beb93cSSam Leffler 	const u8 *addr[2];
82139beb93cSSam Leffler 	size_t len[2];
82239beb93cSSam Leffler 
82339beb93cSSam Leffler 	/*
82439beb93cSSam Leffler 	 * R0-Key-Data = KDF-384(XXKey, "FT-R0",
82539beb93cSSam Leffler 	 *                       SSIDlength || SSID || MDID || R0KHlength ||
82639beb93cSSam Leffler 	 *                       R0KH-ID || S0KH-ID)
82739beb93cSSam Leffler 	 * XXKey is either the second 256 bits of MSK or PSK.
82839beb93cSSam Leffler 	 * PMK-R0 = L(R0-Key-Data, 0, 256)
82939beb93cSSam Leffler 	 * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
83039beb93cSSam Leffler 	 */
83139beb93cSSam Leffler 	if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
83239beb93cSSam Leffler 		return;
83339beb93cSSam Leffler 	pos = buf;
83439beb93cSSam Leffler 	*pos++ = ssid_len;
83539beb93cSSam Leffler 	os_memcpy(pos, ssid, ssid_len);
83639beb93cSSam Leffler 	pos += ssid_len;
83739beb93cSSam Leffler 	os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN);
83839beb93cSSam Leffler 	pos += MOBILITY_DOMAIN_ID_LEN;
83939beb93cSSam Leffler 	*pos++ = r0kh_id_len;
84039beb93cSSam Leffler 	os_memcpy(pos, r0kh_id, r0kh_id_len);
84139beb93cSSam Leffler 	pos += r0kh_id_len;
84239beb93cSSam Leffler 	os_memcpy(pos, s0kh_id, ETH_ALEN);
84339beb93cSSam Leffler 	pos += ETH_ALEN;
84439beb93cSSam Leffler 
84539beb93cSSam Leffler 	sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
84639beb93cSSam Leffler 		   r0_key_data, sizeof(r0_key_data));
84739beb93cSSam Leffler 	os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
84839beb93cSSam Leffler 
84939beb93cSSam Leffler 	/*
85039beb93cSSam Leffler 	 * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
85139beb93cSSam Leffler 	 */
85239beb93cSSam Leffler 	addr[0] = (const u8 *) "FT-R0N";
85339beb93cSSam Leffler 	len[0] = 6;
85439beb93cSSam Leffler 	addr[1] = r0_key_data + PMK_LEN;
85539beb93cSSam Leffler 	len[1] = 16;
85639beb93cSSam Leffler 
85739beb93cSSam Leffler 	sha256_vector(2, addr, len, hash);
85839beb93cSSam Leffler 	os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
85939beb93cSSam Leffler }
86039beb93cSSam Leffler 
86139beb93cSSam Leffler 
86239beb93cSSam Leffler /**
86339beb93cSSam Leffler  * wpa_derive_pmk_r1_name - Derive PMKR1Name
86439beb93cSSam Leffler  *
86539beb93cSSam Leffler  * IEEE Std 802.11r-2008 - 8.5.1.5.4
86639beb93cSSam Leffler  */
86739beb93cSSam Leffler void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
86839beb93cSSam Leffler 			    const u8 *s1kh_id, u8 *pmk_r1_name)
86939beb93cSSam Leffler {
87039beb93cSSam Leffler 	u8 hash[32];
87139beb93cSSam Leffler 	const u8 *addr[4];
87239beb93cSSam Leffler 	size_t len[4];
87339beb93cSSam Leffler 
87439beb93cSSam Leffler 	/*
87539beb93cSSam Leffler 	 * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
87639beb93cSSam Leffler 	 *                                  R1KH-ID || S1KH-ID))
87739beb93cSSam Leffler 	 */
87839beb93cSSam Leffler 	addr[0] = (const u8 *) "FT-R1N";
87939beb93cSSam Leffler 	len[0] = 6;
88039beb93cSSam Leffler 	addr[1] = pmk_r0_name;
88139beb93cSSam Leffler 	len[1] = WPA_PMK_NAME_LEN;
88239beb93cSSam Leffler 	addr[2] = r1kh_id;
88339beb93cSSam Leffler 	len[2] = FT_R1KH_ID_LEN;
88439beb93cSSam Leffler 	addr[3] = s1kh_id;
88539beb93cSSam Leffler 	len[3] = ETH_ALEN;
88639beb93cSSam Leffler 
88739beb93cSSam Leffler 	sha256_vector(4, addr, len, hash);
88839beb93cSSam Leffler 	os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
88939beb93cSSam Leffler }
89039beb93cSSam Leffler 
89139beb93cSSam Leffler 
89239beb93cSSam Leffler /**
89339beb93cSSam Leffler  * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0
89439beb93cSSam Leffler  *
89539beb93cSSam Leffler  * IEEE Std 802.11r-2008 - 8.5.1.5.4
89639beb93cSSam Leffler  */
89739beb93cSSam Leffler void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
89839beb93cSSam Leffler 		       const u8 *r1kh_id, const u8 *s1kh_id,
89939beb93cSSam Leffler 		       u8 *pmk_r1, u8 *pmk_r1_name)
90039beb93cSSam Leffler {
90139beb93cSSam Leffler 	u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
90239beb93cSSam Leffler 	u8 *pos;
90339beb93cSSam Leffler 
90439beb93cSSam Leffler 	/* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
90539beb93cSSam Leffler 	pos = buf;
90639beb93cSSam Leffler 	os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
90739beb93cSSam Leffler 	pos += FT_R1KH_ID_LEN;
90839beb93cSSam Leffler 	os_memcpy(pos, s1kh_id, ETH_ALEN);
90939beb93cSSam Leffler 	pos += ETH_ALEN;
91039beb93cSSam Leffler 
91139beb93cSSam Leffler 	sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
91239beb93cSSam Leffler 
91339beb93cSSam Leffler 	wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
91439beb93cSSam Leffler }
91539beb93cSSam Leffler 
91639beb93cSSam Leffler 
91739beb93cSSam Leffler /**
91839beb93cSSam Leffler  * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1
91939beb93cSSam Leffler  *
92039beb93cSSam Leffler  * IEEE Std 802.11r-2008 - 8.5.1.5.5
92139beb93cSSam Leffler  */
922*5b9c547cSRui Paulo int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
92339beb93cSSam Leffler 		      const u8 *sta_addr, const u8 *bssid,
92439beb93cSSam Leffler 		      const u8 *pmk_r1_name,
925*5b9c547cSRui Paulo 		      struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
92639beb93cSSam Leffler {
92739beb93cSSam Leffler 	u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
92839beb93cSSam Leffler 	u8 *pos, hash[32];
92939beb93cSSam Leffler 	const u8 *addr[6];
93039beb93cSSam Leffler 	size_t len[6];
931*5b9c547cSRui Paulo 	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
932*5b9c547cSRui Paulo 	size_t ptk_len;
93339beb93cSSam Leffler 
93439beb93cSSam Leffler 	/*
93539beb93cSSam Leffler 	 * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
93639beb93cSSam Leffler 	 *                  BSSID || STA-ADDR)
93739beb93cSSam Leffler 	 */
93839beb93cSSam Leffler 	pos = buf;
93939beb93cSSam Leffler 	os_memcpy(pos, snonce, WPA_NONCE_LEN);
94039beb93cSSam Leffler 	pos += WPA_NONCE_LEN;
94139beb93cSSam Leffler 	os_memcpy(pos, anonce, WPA_NONCE_LEN);
94239beb93cSSam Leffler 	pos += WPA_NONCE_LEN;
94339beb93cSSam Leffler 	os_memcpy(pos, bssid, ETH_ALEN);
94439beb93cSSam Leffler 	pos += ETH_ALEN;
94539beb93cSSam Leffler 	os_memcpy(pos, sta_addr, ETH_ALEN);
94639beb93cSSam Leffler 	pos += ETH_ALEN;
94739beb93cSSam Leffler 
948*5b9c547cSRui Paulo 	ptk->kck_len = wpa_kck_len(akmp);
949*5b9c547cSRui Paulo 	ptk->kek_len = wpa_kek_len(akmp);
950*5b9c547cSRui Paulo 	ptk->tk_len = wpa_cipher_key_len(cipher);
951*5b9c547cSRui Paulo 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
952*5b9c547cSRui Paulo 
953*5b9c547cSRui Paulo 	sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
95439beb93cSSam Leffler 
95539beb93cSSam Leffler 	/*
95639beb93cSSam Leffler 	 * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
95739beb93cSSam Leffler 	 *                                ANonce || BSSID || STA-ADDR))
95839beb93cSSam Leffler 	 */
95939beb93cSSam Leffler 	addr[0] = pmk_r1_name;
96039beb93cSSam Leffler 	len[0] = WPA_PMK_NAME_LEN;
96139beb93cSSam Leffler 	addr[1] = (const u8 *) "FT-PTKN";
96239beb93cSSam Leffler 	len[1] = 7;
96339beb93cSSam Leffler 	addr[2] = snonce;
96439beb93cSSam Leffler 	len[2] = WPA_NONCE_LEN;
96539beb93cSSam Leffler 	addr[3] = anonce;
96639beb93cSSam Leffler 	len[3] = WPA_NONCE_LEN;
96739beb93cSSam Leffler 	addr[4] = bssid;
96839beb93cSSam Leffler 	len[4] = ETH_ALEN;
96939beb93cSSam Leffler 	addr[5] = sta_addr;
97039beb93cSSam Leffler 	len[5] = ETH_ALEN;
97139beb93cSSam Leffler 
97239beb93cSSam Leffler 	sha256_vector(6, addr, len, hash);
97339beb93cSSam Leffler 	os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
974*5b9c547cSRui Paulo 
975*5b9c547cSRui Paulo 	os_memcpy(ptk->kck, tmp, ptk->kck_len);
976*5b9c547cSRui Paulo 	os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
977*5b9c547cSRui Paulo 	os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
978*5b9c547cSRui Paulo 
979*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
980*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
981*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
982*5b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
983*5b9c547cSRui Paulo 
984*5b9c547cSRui Paulo 	os_memset(tmp, 0, sizeof(tmp));
985*5b9c547cSRui Paulo 
986*5b9c547cSRui Paulo 	return 0;
98739beb93cSSam Leffler }
98839beb93cSSam Leffler 
98939beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */
990e28a4053SRui Paulo 
991e28a4053SRui Paulo 
992e28a4053SRui Paulo /**
993e28a4053SRui Paulo  * rsn_pmkid - Calculate PMK identifier
994e28a4053SRui Paulo  * @pmk: Pairwise master key
995e28a4053SRui Paulo  * @pmk_len: Length of pmk in bytes
996e28a4053SRui Paulo  * @aa: Authenticator address
997e28a4053SRui Paulo  * @spa: Supplicant address
998e28a4053SRui Paulo  * @pmkid: Buffer for PMKID
999e28a4053SRui Paulo  * @use_sha256: Whether to use SHA256-based KDF
1000e28a4053SRui Paulo  *
1001e28a4053SRui Paulo  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
1002e28a4053SRui Paulo  * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
1003e28a4053SRui Paulo  */
1004e28a4053SRui Paulo void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
1005e28a4053SRui Paulo 	       u8 *pmkid, int use_sha256)
1006e28a4053SRui Paulo {
1007e28a4053SRui Paulo 	char *title = "PMK Name";
1008e28a4053SRui Paulo 	const u8 *addr[3];
1009e28a4053SRui Paulo 	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
1010e28a4053SRui Paulo 	unsigned char hash[SHA256_MAC_LEN];
1011e28a4053SRui Paulo 
1012e28a4053SRui Paulo 	addr[0] = (u8 *) title;
1013e28a4053SRui Paulo 	addr[1] = aa;
1014e28a4053SRui Paulo 	addr[2] = spa;
1015e28a4053SRui Paulo 
1016e28a4053SRui Paulo #ifdef CONFIG_IEEE80211W
1017e28a4053SRui Paulo 	if (use_sha256)
1018e28a4053SRui Paulo 		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
1019e28a4053SRui Paulo 	else
1020e28a4053SRui Paulo #endif /* CONFIG_IEEE80211W */
1021e28a4053SRui Paulo 		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
1022e28a4053SRui Paulo 	os_memcpy(pmkid, hash, PMKID_LEN);
1023e28a4053SRui Paulo }
1024e28a4053SRui Paulo 
1025e28a4053SRui Paulo 
1026*5b9c547cSRui Paulo #ifdef CONFIG_SUITEB
1027*5b9c547cSRui Paulo /**
1028*5b9c547cSRui Paulo  * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM
1029*5b9c547cSRui Paulo  * @kck: Key confirmation key
1030*5b9c547cSRui Paulo  * @kck_len: Length of kck in bytes
1031*5b9c547cSRui Paulo  * @aa: Authenticator address
1032*5b9c547cSRui Paulo  * @spa: Supplicant address
1033*5b9c547cSRui Paulo  * @pmkid: Buffer for PMKID
1034*5b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure
1035*5b9c547cSRui Paulo  *
1036*5b9c547cSRui Paulo  * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
1037*5b9c547cSRui Paulo  * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA))
1038*5b9c547cSRui Paulo  */
1039*5b9c547cSRui Paulo int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
1040*5b9c547cSRui Paulo 		      const u8 *spa, u8 *pmkid)
1041*5b9c547cSRui Paulo {
1042*5b9c547cSRui Paulo 	char *title = "PMK Name";
1043*5b9c547cSRui Paulo 	const u8 *addr[3];
1044*5b9c547cSRui Paulo 	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
1045*5b9c547cSRui Paulo 	unsigned char hash[SHA256_MAC_LEN];
1046*5b9c547cSRui Paulo 
1047*5b9c547cSRui Paulo 	addr[0] = (u8 *) title;
1048*5b9c547cSRui Paulo 	addr[1] = aa;
1049*5b9c547cSRui Paulo 	addr[2] = spa;
1050*5b9c547cSRui Paulo 
1051*5b9c547cSRui Paulo 	if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0)
1052*5b9c547cSRui Paulo 		return -1;
1053*5b9c547cSRui Paulo 	os_memcpy(pmkid, hash, PMKID_LEN);
1054*5b9c547cSRui Paulo 	return 0;
1055*5b9c547cSRui Paulo }
1056*5b9c547cSRui Paulo #endif /* CONFIG_SUITEB */
1057*5b9c547cSRui Paulo 
1058*5b9c547cSRui Paulo 
1059*5b9c547cSRui Paulo #ifdef CONFIG_SUITEB192
1060*5b9c547cSRui Paulo /**
1061*5b9c547cSRui Paulo  * rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM
1062*5b9c547cSRui Paulo  * @kck: Key confirmation key
1063*5b9c547cSRui Paulo  * @kck_len: Length of kck in bytes
1064*5b9c547cSRui Paulo  * @aa: Authenticator address
1065*5b9c547cSRui Paulo  * @spa: Supplicant address
1066*5b9c547cSRui Paulo  * @pmkid: Buffer for PMKID
1067*5b9c547cSRui Paulo  * Returns: 0 on success, -1 on failure
1068*5b9c547cSRui Paulo  *
1069*5b9c547cSRui Paulo  * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
1070*5b9c547cSRui Paulo  * PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA))
1071*5b9c547cSRui Paulo  */
1072*5b9c547cSRui Paulo int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
1073*5b9c547cSRui Paulo 			  const u8 *spa, u8 *pmkid)
1074*5b9c547cSRui Paulo {
1075*5b9c547cSRui Paulo 	char *title = "PMK Name";
1076*5b9c547cSRui Paulo 	const u8 *addr[3];
1077*5b9c547cSRui Paulo 	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
1078*5b9c547cSRui Paulo 	unsigned char hash[SHA384_MAC_LEN];
1079*5b9c547cSRui Paulo 
1080*5b9c547cSRui Paulo 	addr[0] = (u8 *) title;
1081*5b9c547cSRui Paulo 	addr[1] = aa;
1082*5b9c547cSRui Paulo 	addr[2] = spa;
1083*5b9c547cSRui Paulo 
1084*5b9c547cSRui Paulo 	if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0)
1085*5b9c547cSRui Paulo 		return -1;
1086*5b9c547cSRui Paulo 	os_memcpy(pmkid, hash, PMKID_LEN);
1087*5b9c547cSRui Paulo 	return 0;
1088*5b9c547cSRui Paulo }
1089*5b9c547cSRui Paulo #endif /* CONFIG_SUITEB192 */
1090*5b9c547cSRui Paulo 
1091*5b9c547cSRui Paulo 
1092e28a4053SRui Paulo /**
1093e28a4053SRui Paulo  * wpa_cipher_txt - Convert cipher suite to a text string
1094e28a4053SRui Paulo  * @cipher: Cipher suite (WPA_CIPHER_* enum)
1095e28a4053SRui Paulo  * Returns: Pointer to a text string of the cipher suite name
1096e28a4053SRui Paulo  */
1097e28a4053SRui Paulo const char * wpa_cipher_txt(int cipher)
1098e28a4053SRui Paulo {
1099e28a4053SRui Paulo 	switch (cipher) {
1100e28a4053SRui Paulo 	case WPA_CIPHER_NONE:
1101e28a4053SRui Paulo 		return "NONE";
1102e28a4053SRui Paulo 	case WPA_CIPHER_WEP40:
1103e28a4053SRui Paulo 		return "WEP-40";
1104e28a4053SRui Paulo 	case WPA_CIPHER_WEP104:
1105e28a4053SRui Paulo 		return "WEP-104";
1106e28a4053SRui Paulo 	case WPA_CIPHER_TKIP:
1107e28a4053SRui Paulo 		return "TKIP";
1108e28a4053SRui Paulo 	case WPA_CIPHER_CCMP:
1109e28a4053SRui Paulo 		return "CCMP";
1110e28a4053SRui Paulo 	case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
1111e28a4053SRui Paulo 		return "CCMP+TKIP";
1112f05cddf9SRui Paulo 	case WPA_CIPHER_GCMP:
1113f05cddf9SRui Paulo 		return "GCMP";
1114*5b9c547cSRui Paulo 	case WPA_CIPHER_GCMP_256:
1115*5b9c547cSRui Paulo 		return "GCMP-256";
1116*5b9c547cSRui Paulo 	case WPA_CIPHER_CCMP_256:
1117*5b9c547cSRui Paulo 		return "CCMP-256";
1118*5b9c547cSRui Paulo 	case WPA_CIPHER_GTK_NOT_USED:
1119*5b9c547cSRui Paulo 		return "GTK_NOT_USED";
1120e28a4053SRui Paulo 	default:
1121e28a4053SRui Paulo 		return "UNKNOWN";
1122e28a4053SRui Paulo 	}
1123e28a4053SRui Paulo }
1124e28a4053SRui Paulo 
1125e28a4053SRui Paulo 
1126e28a4053SRui Paulo /**
1127e28a4053SRui Paulo  * wpa_key_mgmt_txt - Convert key management suite to a text string
1128e28a4053SRui Paulo  * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
1129e28a4053SRui Paulo  * @proto: WPA/WPA2 version (WPA_PROTO_*)
1130e28a4053SRui Paulo  * Returns: Pointer to a text string of the key management suite name
1131e28a4053SRui Paulo  */
1132e28a4053SRui Paulo const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
1133e28a4053SRui Paulo {
1134e28a4053SRui Paulo 	switch (key_mgmt) {
1135e28a4053SRui Paulo 	case WPA_KEY_MGMT_IEEE8021X:
1136e28a4053SRui Paulo 		if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
1137e28a4053SRui Paulo 			return "WPA2+WPA/IEEE 802.1X/EAP";
1138e28a4053SRui Paulo 		return proto == WPA_PROTO_RSN ?
1139e28a4053SRui Paulo 			"WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
1140e28a4053SRui Paulo 	case WPA_KEY_MGMT_PSK:
1141e28a4053SRui Paulo 		if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
1142e28a4053SRui Paulo 			return "WPA2-PSK+WPA-PSK";
1143e28a4053SRui Paulo 		return proto == WPA_PROTO_RSN ?
1144e28a4053SRui Paulo 			"WPA2-PSK" : "WPA-PSK";
1145e28a4053SRui Paulo 	case WPA_KEY_MGMT_NONE:
1146e28a4053SRui Paulo 		return "NONE";
1147e28a4053SRui Paulo 	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
1148e28a4053SRui Paulo 		return "IEEE 802.1X (no WPA)";
1149e28a4053SRui Paulo #ifdef CONFIG_IEEE80211R
1150e28a4053SRui Paulo 	case WPA_KEY_MGMT_FT_IEEE8021X:
1151e28a4053SRui Paulo 		return "FT-EAP";
1152e28a4053SRui Paulo 	case WPA_KEY_MGMT_FT_PSK:
1153e28a4053SRui Paulo 		return "FT-PSK";
1154e28a4053SRui Paulo #endif /* CONFIG_IEEE80211R */
1155e28a4053SRui Paulo #ifdef CONFIG_IEEE80211W
1156e28a4053SRui Paulo 	case WPA_KEY_MGMT_IEEE8021X_SHA256:
1157e28a4053SRui Paulo 		return "WPA2-EAP-SHA256";
1158e28a4053SRui Paulo 	case WPA_KEY_MGMT_PSK_SHA256:
1159e28a4053SRui Paulo 		return "WPA2-PSK-SHA256";
1160e28a4053SRui Paulo #endif /* CONFIG_IEEE80211W */
1161*5b9c547cSRui Paulo 	case WPA_KEY_MGMT_WPS:
1162*5b9c547cSRui Paulo 		return "WPS";
1163*5b9c547cSRui Paulo 	case WPA_KEY_MGMT_SAE:
1164*5b9c547cSRui Paulo 		return "SAE";
1165*5b9c547cSRui Paulo 	case WPA_KEY_MGMT_FT_SAE:
1166*5b9c547cSRui Paulo 		return "FT-SAE";
1167*5b9c547cSRui Paulo 	case WPA_KEY_MGMT_OSEN:
1168*5b9c547cSRui Paulo 		return "OSEN";
1169*5b9c547cSRui Paulo 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
1170*5b9c547cSRui Paulo 		return "WPA2-EAP-SUITE-B";
1171*5b9c547cSRui Paulo 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
1172*5b9c547cSRui Paulo 		return "WPA2-EAP-SUITE-B-192";
1173e28a4053SRui Paulo 	default:
1174e28a4053SRui Paulo 		return "UNKNOWN";
1175e28a4053SRui Paulo 	}
1176e28a4053SRui Paulo }
1177e28a4053SRui Paulo 
1178e28a4053SRui Paulo 
1179*5b9c547cSRui Paulo u32 wpa_akm_to_suite(int akm)
1180*5b9c547cSRui Paulo {
1181*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
1182*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_FT_8021X;
1183*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_FT_PSK)
1184*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_FT_PSK;
1185*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_IEEE8021X)
1186*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_8021X;
1187*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
1188*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_8021X_SHA256;
1189*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_IEEE8021X)
1190*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_8021X;
1191*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_PSK_SHA256)
1192*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_PSK_SHA256;
1193*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_PSK)
1194*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_PSK;
1195*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_CCKM)
1196*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_CCKM;
1197*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_OSEN)
1198*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_OSEN;
1199*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
1200*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_8021X_SUITE_B;
1201*5b9c547cSRui Paulo 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
1202*5b9c547cSRui Paulo 		return WLAN_AKM_SUITE_8021X_SUITE_B_192;
1203*5b9c547cSRui Paulo 	return 0;
1204*5b9c547cSRui Paulo }
1205*5b9c547cSRui Paulo 
1206*5b9c547cSRui Paulo 
1207e28a4053SRui Paulo int wpa_compare_rsn_ie(int ft_initial_assoc,
1208e28a4053SRui Paulo 		       const u8 *ie1, size_t ie1len,
1209e28a4053SRui Paulo 		       const u8 *ie2, size_t ie2len)
1210e28a4053SRui Paulo {
1211e28a4053SRui Paulo 	if (ie1 == NULL || ie2 == NULL)
1212e28a4053SRui Paulo 		return -1;
1213e28a4053SRui Paulo 
1214e28a4053SRui Paulo 	if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
1215e28a4053SRui Paulo 		return 0; /* identical IEs */
1216e28a4053SRui Paulo 
1217e28a4053SRui Paulo #ifdef CONFIG_IEEE80211R
1218e28a4053SRui Paulo 	if (ft_initial_assoc) {
1219e28a4053SRui Paulo 		struct wpa_ie_data ie1d, ie2d;
1220e28a4053SRui Paulo 		/*
1221e28a4053SRui Paulo 		 * The PMKID-List in RSN IE is different between Beacon/Probe
1222e28a4053SRui Paulo 		 * Response/(Re)Association Request frames and EAPOL-Key
1223e28a4053SRui Paulo 		 * messages in FT initial mobility domain association. Allow
1224e28a4053SRui Paulo 		 * for this, but verify that other parts of the RSN IEs are
1225e28a4053SRui Paulo 		 * identical.
1226e28a4053SRui Paulo 		 */
1227e28a4053SRui Paulo 		if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
1228e28a4053SRui Paulo 		    wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
1229e28a4053SRui Paulo 			return -1;
1230e28a4053SRui Paulo 		if (ie1d.proto == ie2d.proto &&
1231e28a4053SRui Paulo 		    ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
1232e28a4053SRui Paulo 		    ie1d.group_cipher == ie2d.group_cipher &&
1233e28a4053SRui Paulo 		    ie1d.key_mgmt == ie2d.key_mgmt &&
1234e28a4053SRui Paulo 		    ie1d.capabilities == ie2d.capabilities &&
1235e28a4053SRui Paulo 		    ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
1236e28a4053SRui Paulo 			return 0;
1237e28a4053SRui Paulo 	}
1238e28a4053SRui Paulo #endif /* CONFIG_IEEE80211R */
1239e28a4053SRui Paulo 
1240e28a4053SRui Paulo 	return -1;
1241e28a4053SRui Paulo }
1242e28a4053SRui Paulo 
1243e28a4053SRui Paulo 
1244e28a4053SRui Paulo #ifdef CONFIG_IEEE80211R
1245e28a4053SRui Paulo int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
1246e28a4053SRui Paulo {
1247e28a4053SRui Paulo 	u8 *start, *end, *rpos, *rend;
1248e28a4053SRui Paulo 	int added = 0;
1249e28a4053SRui Paulo 
1250e28a4053SRui Paulo 	start = ies;
1251e28a4053SRui Paulo 	end = ies + ies_len;
1252e28a4053SRui Paulo 
1253e28a4053SRui Paulo 	while (start < end) {
1254e28a4053SRui Paulo 		if (*start == WLAN_EID_RSN)
1255e28a4053SRui Paulo 			break;
1256e28a4053SRui Paulo 		start += 2 + start[1];
1257e28a4053SRui Paulo 	}
1258e28a4053SRui Paulo 	if (start >= end) {
1259e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
1260e28a4053SRui Paulo 			   "IEs data");
1261e28a4053SRui Paulo 		return -1;
1262e28a4053SRui Paulo 	}
1263e28a4053SRui Paulo 	wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
1264e28a4053SRui Paulo 		    start, 2 + start[1]);
1265e28a4053SRui Paulo 
1266e28a4053SRui Paulo 	/* Find start of PMKID-Count */
1267e28a4053SRui Paulo 	rpos = start + 2;
1268e28a4053SRui Paulo 	rend = rpos + start[1];
1269e28a4053SRui Paulo 
1270e28a4053SRui Paulo 	/* Skip Version and Group Data Cipher Suite */
1271e28a4053SRui Paulo 	rpos += 2 + 4;
1272e28a4053SRui Paulo 	/* Skip Pairwise Cipher Suite Count and List */
1273e28a4053SRui Paulo 	rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
1274e28a4053SRui Paulo 	/* Skip AKM Suite Count and List */
1275e28a4053SRui Paulo 	rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
1276e28a4053SRui Paulo 
1277e28a4053SRui Paulo 	if (rpos == rend) {
1278e28a4053SRui Paulo 		/* Add RSN Capabilities */
1279e28a4053SRui Paulo 		os_memmove(rpos + 2, rpos, end - rpos);
1280e28a4053SRui Paulo 		*rpos++ = 0;
1281e28a4053SRui Paulo 		*rpos++ = 0;
1282e28a4053SRui Paulo 	} else {
1283e28a4053SRui Paulo 		/* Skip RSN Capabilities */
1284e28a4053SRui Paulo 		rpos += 2;
1285e28a4053SRui Paulo 		if (rpos > rend) {
1286e28a4053SRui Paulo 			wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
1287e28a4053SRui Paulo 				   "IEs data");
1288e28a4053SRui Paulo 			return -1;
1289e28a4053SRui Paulo 		}
1290e28a4053SRui Paulo 	}
1291e28a4053SRui Paulo 
1292e28a4053SRui Paulo 	if (rpos == rend) {
1293e28a4053SRui Paulo 		/* No PMKID-Count field included; add it */
1294e28a4053SRui Paulo 		os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
1295e28a4053SRui Paulo 		WPA_PUT_LE16(rpos, 1);
1296e28a4053SRui Paulo 		rpos += 2;
1297e28a4053SRui Paulo 		os_memcpy(rpos, pmkid, PMKID_LEN);
1298e28a4053SRui Paulo 		added += 2 + PMKID_LEN;
1299e28a4053SRui Paulo 		start[1] += 2 + PMKID_LEN;
1300e28a4053SRui Paulo 	} else {
1301e28a4053SRui Paulo 		/* PMKID-Count was included; use it */
1302e28a4053SRui Paulo 		if (WPA_GET_LE16(rpos) != 0) {
1303e28a4053SRui Paulo 			wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
1304e28a4053SRui Paulo 				   "in RSN IE in EAPOL-Key data");
1305e28a4053SRui Paulo 			return -1;
1306e28a4053SRui Paulo 		}
1307e28a4053SRui Paulo 		WPA_PUT_LE16(rpos, 1);
1308e28a4053SRui Paulo 		rpos += 2;
1309e28a4053SRui Paulo 		os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
1310e28a4053SRui Paulo 		os_memcpy(rpos, pmkid, PMKID_LEN);
1311e28a4053SRui Paulo 		added += PMKID_LEN;
1312e28a4053SRui Paulo 		start[1] += PMKID_LEN;
1313e28a4053SRui Paulo 	}
1314e28a4053SRui Paulo 
1315e28a4053SRui Paulo 	wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
1316e28a4053SRui Paulo 		    "(PMKID inserted)", start, 2 + start[1]);
1317e28a4053SRui Paulo 
1318e28a4053SRui Paulo 	return added;
1319e28a4053SRui Paulo }
1320e28a4053SRui Paulo #endif /* CONFIG_IEEE80211R */
1321f05cddf9SRui Paulo 
1322f05cddf9SRui Paulo 
1323f05cddf9SRui Paulo int wpa_cipher_key_len(int cipher)
1324f05cddf9SRui Paulo {
1325f05cddf9SRui Paulo 	switch (cipher) {
1326*5b9c547cSRui Paulo 	case WPA_CIPHER_CCMP_256:
1327*5b9c547cSRui Paulo 	case WPA_CIPHER_GCMP_256:
1328*5b9c547cSRui Paulo 	case WPA_CIPHER_BIP_GMAC_256:
1329*5b9c547cSRui Paulo 	case WPA_CIPHER_BIP_CMAC_256:
1330*5b9c547cSRui Paulo 		return 32;
1331f05cddf9SRui Paulo 	case WPA_CIPHER_CCMP:
1332f05cddf9SRui Paulo 	case WPA_CIPHER_GCMP:
1333*5b9c547cSRui Paulo 	case WPA_CIPHER_AES_128_CMAC:
1334*5b9c547cSRui Paulo 	case WPA_CIPHER_BIP_GMAC_128:
1335f05cddf9SRui Paulo 		return 16;
1336f05cddf9SRui Paulo 	case WPA_CIPHER_TKIP:
1337f05cddf9SRui Paulo 		return 32;
1338f05cddf9SRui Paulo 	case WPA_CIPHER_WEP104:
1339f05cddf9SRui Paulo 		return 13;
1340f05cddf9SRui Paulo 	case WPA_CIPHER_WEP40:
1341f05cddf9SRui Paulo 		return 5;
1342f05cddf9SRui Paulo 	}
1343f05cddf9SRui Paulo 
1344f05cddf9SRui Paulo 	return 0;
1345f05cddf9SRui Paulo }
1346f05cddf9SRui Paulo 
1347f05cddf9SRui Paulo 
1348f05cddf9SRui Paulo int wpa_cipher_rsc_len(int cipher)
1349f05cddf9SRui Paulo {
1350f05cddf9SRui Paulo 	switch (cipher) {
1351*5b9c547cSRui Paulo 	case WPA_CIPHER_CCMP_256:
1352*5b9c547cSRui Paulo 	case WPA_CIPHER_GCMP_256:
1353f05cddf9SRui Paulo 	case WPA_CIPHER_CCMP:
1354f05cddf9SRui Paulo 	case WPA_CIPHER_GCMP:
1355f05cddf9SRui Paulo 	case WPA_CIPHER_TKIP:
1356f05cddf9SRui Paulo 		return 6;
1357f05cddf9SRui Paulo 	case WPA_CIPHER_WEP104:
1358f05cddf9SRui Paulo 	case WPA_CIPHER_WEP40:
1359f05cddf9SRui Paulo 		return 0;
1360f05cddf9SRui Paulo 	}
1361f05cddf9SRui Paulo 
1362f05cddf9SRui Paulo 	return 0;
1363f05cddf9SRui Paulo }
1364f05cddf9SRui Paulo 
1365f05cddf9SRui Paulo 
1366f05cddf9SRui Paulo int wpa_cipher_to_alg(int cipher)
1367f05cddf9SRui Paulo {
1368f05cddf9SRui Paulo 	switch (cipher) {
1369*5b9c547cSRui Paulo 	case WPA_CIPHER_CCMP_256:
1370*5b9c547cSRui Paulo 		return WPA_ALG_CCMP_256;
1371*5b9c547cSRui Paulo 	case WPA_CIPHER_GCMP_256:
1372*5b9c547cSRui Paulo 		return WPA_ALG_GCMP_256;
1373f05cddf9SRui Paulo 	case WPA_CIPHER_CCMP:
1374f05cddf9SRui Paulo 		return WPA_ALG_CCMP;
1375f05cddf9SRui Paulo 	case WPA_CIPHER_GCMP:
1376f05cddf9SRui Paulo 		return WPA_ALG_GCMP;
1377f05cddf9SRui Paulo 	case WPA_CIPHER_TKIP:
1378f05cddf9SRui Paulo 		return WPA_ALG_TKIP;
1379f05cddf9SRui Paulo 	case WPA_CIPHER_WEP104:
1380f05cddf9SRui Paulo 	case WPA_CIPHER_WEP40:
1381f05cddf9SRui Paulo 		return WPA_ALG_WEP;
1382*5b9c547cSRui Paulo 	case WPA_CIPHER_AES_128_CMAC:
1383*5b9c547cSRui Paulo 		return WPA_ALG_IGTK;
1384*5b9c547cSRui Paulo 	case WPA_CIPHER_BIP_GMAC_128:
1385*5b9c547cSRui Paulo 		return WPA_ALG_BIP_GMAC_128;
1386*5b9c547cSRui Paulo 	case WPA_CIPHER_BIP_GMAC_256:
1387*5b9c547cSRui Paulo 		return WPA_ALG_BIP_GMAC_256;
1388*5b9c547cSRui Paulo 	case WPA_CIPHER_BIP_CMAC_256:
1389*5b9c547cSRui Paulo 		return WPA_ALG_BIP_CMAC_256;
1390f05cddf9SRui Paulo 	}
1391f05cddf9SRui Paulo 	return WPA_ALG_NONE;
1392f05cddf9SRui Paulo }
1393f05cddf9SRui Paulo 
1394f05cddf9SRui Paulo 
1395f05cddf9SRui Paulo int wpa_cipher_valid_pairwise(int cipher)
1396f05cddf9SRui Paulo {
1397*5b9c547cSRui Paulo 	return cipher == WPA_CIPHER_CCMP_256 ||
1398*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_GCMP_256 ||
1399*5b9c547cSRui Paulo 		cipher == WPA_CIPHER_CCMP ||
1400f05cddf9SRui Paulo 		cipher == WPA_CIPHER_GCMP ||
1401f05cddf9SRui Paulo 		cipher == WPA_CIPHER_TKIP;
1402f05cddf9SRui Paulo }
1403f05cddf9SRui Paulo 
1404f05cddf9SRui Paulo 
1405f05cddf9SRui Paulo u32 wpa_cipher_to_suite(int proto, int cipher)
1406f05cddf9SRui Paulo {
1407*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_CCMP_256)
1408*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_CCMP_256;
1409*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_GCMP_256)
1410*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_GCMP_256;
1411f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_CCMP)
1412f05cddf9SRui Paulo 		return (proto == WPA_PROTO_RSN ?
1413f05cddf9SRui Paulo 			RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
1414f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_GCMP)
1415f05cddf9SRui Paulo 		return RSN_CIPHER_SUITE_GCMP;
1416f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_TKIP)
1417f05cddf9SRui Paulo 		return (proto == WPA_PROTO_RSN ?
1418f05cddf9SRui Paulo 			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
1419f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_WEP104)
1420f05cddf9SRui Paulo 		return (proto == WPA_PROTO_RSN ?
1421f05cddf9SRui Paulo 			RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
1422f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_WEP40)
1423f05cddf9SRui Paulo 		return (proto == WPA_PROTO_RSN ?
1424f05cddf9SRui Paulo 			RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
1425f05cddf9SRui Paulo 	if (cipher & WPA_CIPHER_NONE)
1426f05cddf9SRui Paulo 		return (proto == WPA_PROTO_RSN ?
1427f05cddf9SRui Paulo 			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
1428*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_GTK_NOT_USED)
1429*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
1430*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_AES_128_CMAC)
1431*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_AES_128_CMAC;
1432*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_BIP_GMAC_128)
1433*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_BIP_GMAC_128;
1434*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_BIP_GMAC_256)
1435*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_BIP_GMAC_256;
1436*5b9c547cSRui Paulo 	if (cipher & WPA_CIPHER_BIP_CMAC_256)
1437*5b9c547cSRui Paulo 		return RSN_CIPHER_SUITE_BIP_CMAC_256;
1438f05cddf9SRui Paulo 	return 0;
1439f05cddf9SRui Paulo }
1440f05cddf9SRui Paulo 
1441f05cddf9SRui Paulo 
1442*5b9c547cSRui Paulo int rsn_cipher_put_suites(u8 *start, int ciphers)
1443f05cddf9SRui Paulo {
1444*5b9c547cSRui Paulo 	u8 *pos = start;
1445f05cddf9SRui Paulo 
1446*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP_256) {
1447*5b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256);
1448*5b9c547cSRui Paulo 		pos += RSN_SELECTOR_LEN;
1449*5b9c547cSRui Paulo 	}
1450*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP_256) {
1451*5b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256);
1452*5b9c547cSRui Paulo 		pos += RSN_SELECTOR_LEN;
1453*5b9c547cSRui Paulo 	}
1454f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_CCMP) {
1455f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
1456f05cddf9SRui Paulo 		pos += RSN_SELECTOR_LEN;
1457f05cddf9SRui Paulo 	}
1458f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_GCMP) {
1459f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
1460f05cddf9SRui Paulo 		pos += RSN_SELECTOR_LEN;
1461f05cddf9SRui Paulo 	}
1462f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_TKIP) {
1463f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
1464f05cddf9SRui Paulo 		pos += RSN_SELECTOR_LEN;
1465f05cddf9SRui Paulo 	}
1466f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_NONE) {
1467f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
1468f05cddf9SRui Paulo 		pos += RSN_SELECTOR_LEN;
1469f05cddf9SRui Paulo 	}
1470f05cddf9SRui Paulo 
1471*5b9c547cSRui Paulo 	return (pos - start) / RSN_SELECTOR_LEN;
1472f05cddf9SRui Paulo }
1473f05cddf9SRui Paulo 
1474f05cddf9SRui Paulo 
1475*5b9c547cSRui Paulo int wpa_cipher_put_suites(u8 *start, int ciphers)
1476f05cddf9SRui Paulo {
1477*5b9c547cSRui Paulo 	u8 *pos = start;
1478f05cddf9SRui Paulo 
1479f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_CCMP) {
1480f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
1481f05cddf9SRui Paulo 		pos += WPA_SELECTOR_LEN;
1482f05cddf9SRui Paulo 	}
1483f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_TKIP) {
1484f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
1485f05cddf9SRui Paulo 		pos += WPA_SELECTOR_LEN;
1486f05cddf9SRui Paulo 	}
1487f05cddf9SRui Paulo 	if (ciphers & WPA_CIPHER_NONE) {
1488f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
1489f05cddf9SRui Paulo 		pos += WPA_SELECTOR_LEN;
1490f05cddf9SRui Paulo 	}
1491f05cddf9SRui Paulo 
1492*5b9c547cSRui Paulo 	return (pos - start) / RSN_SELECTOR_LEN;
1493*5b9c547cSRui Paulo }
1494*5b9c547cSRui Paulo 
1495*5b9c547cSRui Paulo 
1496*5b9c547cSRui Paulo int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
1497*5b9c547cSRui Paulo {
1498*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP_256)
1499*5b9c547cSRui Paulo 		return WPA_CIPHER_CCMP_256;
1500*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP_256)
1501*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP_256;
1502*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP)
1503*5b9c547cSRui Paulo 		return WPA_CIPHER_CCMP;
1504*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP)
1505*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP;
1506*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_TKIP)
1507*5b9c547cSRui Paulo 		return WPA_CIPHER_TKIP;
1508*5b9c547cSRui Paulo 	if (none_allowed && (ciphers & WPA_CIPHER_NONE))
1509*5b9c547cSRui Paulo 		return WPA_CIPHER_NONE;
1510*5b9c547cSRui Paulo 	return -1;
1511*5b9c547cSRui Paulo }
1512*5b9c547cSRui Paulo 
1513*5b9c547cSRui Paulo 
1514*5b9c547cSRui Paulo int wpa_pick_group_cipher(int ciphers)
1515*5b9c547cSRui Paulo {
1516*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP_256)
1517*5b9c547cSRui Paulo 		return WPA_CIPHER_CCMP_256;
1518*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP_256)
1519*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP_256;
1520*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP)
1521*5b9c547cSRui Paulo 		return WPA_CIPHER_CCMP;
1522*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP)
1523*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP;
1524*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GTK_NOT_USED)
1525*5b9c547cSRui Paulo 		return WPA_CIPHER_GTK_NOT_USED;
1526*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_TKIP)
1527*5b9c547cSRui Paulo 		return WPA_CIPHER_TKIP;
1528*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_WEP104)
1529*5b9c547cSRui Paulo 		return WPA_CIPHER_WEP104;
1530*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_WEP40)
1531*5b9c547cSRui Paulo 		return WPA_CIPHER_WEP40;
1532*5b9c547cSRui Paulo 	return -1;
1533*5b9c547cSRui Paulo }
1534*5b9c547cSRui Paulo 
1535*5b9c547cSRui Paulo 
1536*5b9c547cSRui Paulo int wpa_parse_cipher(const char *value)
1537*5b9c547cSRui Paulo {
1538*5b9c547cSRui Paulo 	int val = 0, last;
1539*5b9c547cSRui Paulo 	char *start, *end, *buf;
1540*5b9c547cSRui Paulo 
1541*5b9c547cSRui Paulo 	buf = os_strdup(value);
1542*5b9c547cSRui Paulo 	if (buf == NULL)
1543*5b9c547cSRui Paulo 		return -1;
1544*5b9c547cSRui Paulo 	start = buf;
1545*5b9c547cSRui Paulo 
1546*5b9c547cSRui Paulo 	while (*start != '\0') {
1547*5b9c547cSRui Paulo 		while (*start == ' ' || *start == '\t')
1548*5b9c547cSRui Paulo 			start++;
1549*5b9c547cSRui Paulo 		if (*start == '\0')
1550*5b9c547cSRui Paulo 			break;
1551*5b9c547cSRui Paulo 		end = start;
1552*5b9c547cSRui Paulo 		while (*end != ' ' && *end != '\t' && *end != '\0')
1553*5b9c547cSRui Paulo 			end++;
1554*5b9c547cSRui Paulo 		last = *end == '\0';
1555*5b9c547cSRui Paulo 		*end = '\0';
1556*5b9c547cSRui Paulo 		if (os_strcmp(start, "CCMP-256") == 0)
1557*5b9c547cSRui Paulo 			val |= WPA_CIPHER_CCMP_256;
1558*5b9c547cSRui Paulo 		else if (os_strcmp(start, "GCMP-256") == 0)
1559*5b9c547cSRui Paulo 			val |= WPA_CIPHER_GCMP_256;
1560*5b9c547cSRui Paulo 		else if (os_strcmp(start, "CCMP") == 0)
1561*5b9c547cSRui Paulo 			val |= WPA_CIPHER_CCMP;
1562*5b9c547cSRui Paulo 		else if (os_strcmp(start, "GCMP") == 0)
1563*5b9c547cSRui Paulo 			val |= WPA_CIPHER_GCMP;
1564*5b9c547cSRui Paulo 		else if (os_strcmp(start, "TKIP") == 0)
1565*5b9c547cSRui Paulo 			val |= WPA_CIPHER_TKIP;
1566*5b9c547cSRui Paulo 		else if (os_strcmp(start, "WEP104") == 0)
1567*5b9c547cSRui Paulo 			val |= WPA_CIPHER_WEP104;
1568*5b9c547cSRui Paulo 		else if (os_strcmp(start, "WEP40") == 0)
1569*5b9c547cSRui Paulo 			val |= WPA_CIPHER_WEP40;
1570*5b9c547cSRui Paulo 		else if (os_strcmp(start, "NONE") == 0)
1571*5b9c547cSRui Paulo 			val |= WPA_CIPHER_NONE;
1572*5b9c547cSRui Paulo 		else if (os_strcmp(start, "GTK_NOT_USED") == 0)
1573*5b9c547cSRui Paulo 			val |= WPA_CIPHER_GTK_NOT_USED;
1574*5b9c547cSRui Paulo 		else {
1575*5b9c547cSRui Paulo 			os_free(buf);
1576*5b9c547cSRui Paulo 			return -1;
1577*5b9c547cSRui Paulo 		}
1578*5b9c547cSRui Paulo 
1579*5b9c547cSRui Paulo 		if (last)
1580*5b9c547cSRui Paulo 			break;
1581*5b9c547cSRui Paulo 		start = end + 1;
1582*5b9c547cSRui Paulo 	}
1583*5b9c547cSRui Paulo 	os_free(buf);
1584*5b9c547cSRui Paulo 
1585*5b9c547cSRui Paulo 	return val;
1586*5b9c547cSRui Paulo }
1587*5b9c547cSRui Paulo 
1588*5b9c547cSRui Paulo 
1589*5b9c547cSRui Paulo int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
1590*5b9c547cSRui Paulo {
1591*5b9c547cSRui Paulo 	char *pos = start;
1592*5b9c547cSRui Paulo 	int ret;
1593*5b9c547cSRui Paulo 
1594*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP_256) {
1595*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sCCMP-256",
1596*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1597*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1598*5b9c547cSRui Paulo 			return -1;
1599*5b9c547cSRui Paulo 		pos += ret;
1600*5b9c547cSRui Paulo 	}
1601*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP_256) {
1602*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sGCMP-256",
1603*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1604*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1605*5b9c547cSRui Paulo 			return -1;
1606*5b9c547cSRui Paulo 		pos += ret;
1607*5b9c547cSRui Paulo 	}
1608*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_CCMP) {
1609*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sCCMP",
1610*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1611*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1612*5b9c547cSRui Paulo 			return -1;
1613*5b9c547cSRui Paulo 		pos += ret;
1614*5b9c547cSRui Paulo 	}
1615*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_GCMP) {
1616*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sGCMP",
1617*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1618*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1619*5b9c547cSRui Paulo 			return -1;
1620*5b9c547cSRui Paulo 		pos += ret;
1621*5b9c547cSRui Paulo 	}
1622*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_TKIP) {
1623*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sTKIP",
1624*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1625*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1626*5b9c547cSRui Paulo 			return -1;
1627*5b9c547cSRui Paulo 		pos += ret;
1628*5b9c547cSRui Paulo 	}
1629*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_WEP104) {
1630*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sWEP104",
1631*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1632*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1633*5b9c547cSRui Paulo 			return -1;
1634*5b9c547cSRui Paulo 		pos += ret;
1635*5b9c547cSRui Paulo 	}
1636*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_WEP40) {
1637*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sWEP40",
1638*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1639*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1640*5b9c547cSRui Paulo 			return -1;
1641*5b9c547cSRui Paulo 		pos += ret;
1642*5b9c547cSRui Paulo 	}
1643*5b9c547cSRui Paulo 	if (ciphers & WPA_CIPHER_NONE) {
1644*5b9c547cSRui Paulo 		ret = os_snprintf(pos, end - pos, "%sNONE",
1645*5b9c547cSRui Paulo 				  pos == start ? "" : delim);
1646*5b9c547cSRui Paulo 		if (os_snprintf_error(end - pos, ret))
1647*5b9c547cSRui Paulo 			return -1;
1648*5b9c547cSRui Paulo 		pos += ret;
1649*5b9c547cSRui Paulo 	}
1650*5b9c547cSRui Paulo 
1651*5b9c547cSRui Paulo 	return pos - start;
1652*5b9c547cSRui Paulo }
1653*5b9c547cSRui Paulo 
1654*5b9c547cSRui Paulo 
1655*5b9c547cSRui Paulo int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
1656*5b9c547cSRui Paulo {
1657*5b9c547cSRui Paulo 	int pairwise = 0;
1658*5b9c547cSRui Paulo 
1659*5b9c547cSRui Paulo 	/* Select group cipher based on the enabled pairwise cipher suites */
1660*5b9c547cSRui Paulo 	if (wpa & 1)
1661*5b9c547cSRui Paulo 		pairwise |= wpa_pairwise;
1662*5b9c547cSRui Paulo 	if (wpa & 2)
1663*5b9c547cSRui Paulo 		pairwise |= rsn_pairwise;
1664*5b9c547cSRui Paulo 
1665*5b9c547cSRui Paulo 	if (pairwise & WPA_CIPHER_TKIP)
1666*5b9c547cSRui Paulo 		return WPA_CIPHER_TKIP;
1667*5b9c547cSRui Paulo 	if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
1668*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP;
1669*5b9c547cSRui Paulo 	if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP |
1670*5b9c547cSRui Paulo 			 WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256)
1671*5b9c547cSRui Paulo 		return WPA_CIPHER_GCMP_256;
1672*5b9c547cSRui Paulo 	if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
1673*5b9c547cSRui Paulo 			 WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256)
1674*5b9c547cSRui Paulo 		return WPA_CIPHER_CCMP_256;
1675*5b9c547cSRui Paulo 	return WPA_CIPHER_CCMP;
1676f05cddf9SRui Paulo }
1677