xref: /freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * wpa_supplicant - WPA/RSN IE and KDE processing
385732ac8SCy Schubert  * Copyright (c) 2003-2018, 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"
1239beb93cSSam Leffler #include "wpa.h"
1339beb93cSSam Leffler #include "pmksa_cache.h"
14e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
1539beb93cSSam Leffler #include "wpa_i.h"
1639beb93cSSam Leffler #include "wpa_ie.h"
1739beb93cSSam Leffler 
1839beb93cSSam Leffler 
1939beb93cSSam Leffler /**
2039beb93cSSam Leffler  * wpa_parse_wpa_ie - Parse WPA/RSN IE
2139beb93cSSam Leffler  * @wpa_ie: Pointer to WPA or RSN IE
2239beb93cSSam Leffler  * @wpa_ie_len: Length of the WPA/RSN IE
2339beb93cSSam Leffler  * @data: Pointer to data area for parsing results
2439beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
2539beb93cSSam Leffler  *
2639beb93cSSam Leffler  * Parse the contents of WPA or RSN IE and write the parsed data into data.
2739beb93cSSam Leffler  */
wpa_parse_wpa_ie(const u8 * wpa_ie,size_t wpa_ie_len,struct wpa_ie_data * data)2839beb93cSSam Leffler int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
2939beb93cSSam Leffler 		     struct wpa_ie_data *data)
3039beb93cSSam Leffler {
3139beb93cSSam Leffler 	if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
3239beb93cSSam Leffler 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
33325151a3SRui Paulo 	if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
34325151a3SRui Paulo 	    wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
35325151a3SRui Paulo 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
3639beb93cSSam Leffler 	else
3739beb93cSSam Leffler 		return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
3839beb93cSSam Leffler }
3939beb93cSSam Leffler 
4039beb93cSSam Leffler 
wpa_gen_wpa_ie_wpa(u8 * wpa_ie,size_t wpa_ie_len,int pairwise_cipher,int group_cipher,int key_mgmt)4139beb93cSSam Leffler static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
4239beb93cSSam Leffler 			      int pairwise_cipher, int group_cipher,
4339beb93cSSam Leffler 			      int key_mgmt)
4439beb93cSSam Leffler {
4539beb93cSSam Leffler 	u8 *pos;
4639beb93cSSam Leffler 	struct wpa_ie_hdr *hdr;
47f05cddf9SRui Paulo 	u32 suite;
4839beb93cSSam Leffler 
4939beb93cSSam Leffler 	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
5039beb93cSSam Leffler 	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
5139beb93cSSam Leffler 		return -1;
5239beb93cSSam Leffler 
5339beb93cSSam Leffler 	hdr = (struct wpa_ie_hdr *) wpa_ie;
5439beb93cSSam Leffler 	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
5539beb93cSSam Leffler 	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
5639beb93cSSam Leffler 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
5739beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
5839beb93cSSam Leffler 
59f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
60f05cddf9SRui Paulo 	if (suite == 0) {
6139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
6239beb93cSSam Leffler 			   group_cipher);
6339beb93cSSam Leffler 		return -1;
6439beb93cSSam Leffler 	}
65f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
6639beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
6739beb93cSSam Leffler 
6839beb93cSSam Leffler 	*pos++ = 1;
6939beb93cSSam Leffler 	*pos++ = 0;
70f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
71f05cddf9SRui Paulo 	if (suite == 0 ||
72f05cddf9SRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
73f05cddf9SRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
7439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
7539beb93cSSam Leffler 			   pairwise_cipher);
7639beb93cSSam Leffler 		return -1;
7739beb93cSSam Leffler 	}
78f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
7939beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
8039beb93cSSam Leffler 
8139beb93cSSam Leffler 	*pos++ = 1;
8239beb93cSSam Leffler 	*pos++ = 0;
8339beb93cSSam Leffler 	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
8439beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
8539beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
8639beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
8739beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
8839beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
89f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
90f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
9139beb93cSSam Leffler 	} else {
9239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
9339beb93cSSam Leffler 			   key_mgmt);
9439beb93cSSam Leffler 		return -1;
9539beb93cSSam Leffler 	}
9639beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
9739beb93cSSam Leffler 
9839beb93cSSam Leffler 	/* WPA Capabilities; use defaults, so no need to include it */
9939beb93cSSam Leffler 
10039beb93cSSam Leffler 	hdr->len = (pos - wpa_ie) - 2;
10139beb93cSSam Leffler 
10239beb93cSSam Leffler 	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
10339beb93cSSam Leffler 
10439beb93cSSam Leffler 	return pos - wpa_ie;
10539beb93cSSam Leffler }
10639beb93cSSam Leffler 
10739beb93cSSam Leffler 
rsn_supp_capab(struct wpa_sm * sm)108c1d255d3SCy Schubert u16 rsn_supp_capab(struct wpa_sm *sm)
109c1d255d3SCy Schubert {
110c1d255d3SCy Schubert 	u16 capab = 0;
111c1d255d3SCy Schubert 
112*a90b9d01SCy Schubert 	if (sm->wmm_enabled) {
113*a90b9d01SCy Schubert 		/* Advertise 16 PTKSA replay counters when using WMM */
114*a90b9d01SCy Schubert 		capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
115*a90b9d01SCy Schubert 	}
116c1d255d3SCy Schubert 	if (sm->mfp)
117c1d255d3SCy Schubert 		capab |= WPA_CAPABILITY_MFPC;
118c1d255d3SCy Schubert 	if (sm->mfp == 2)
119c1d255d3SCy Schubert 		capab |= WPA_CAPABILITY_MFPR;
120c1d255d3SCy Schubert 	if (sm->ocv)
121c1d255d3SCy Schubert 		capab |= WPA_CAPABILITY_OCVC;
122c1d255d3SCy Schubert 	if (sm->ext_key_id)
123c1d255d3SCy Schubert 		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
124c1d255d3SCy Schubert 
125c1d255d3SCy Schubert 	return capab;
126c1d255d3SCy Schubert }
127c1d255d3SCy Schubert 
128c1d255d3SCy Schubert 
wpa_gen_wpa_ie_rsn(u8 * rsn_ie,size_t rsn_ie_len,int pairwise_cipher,int group_cipher,int key_mgmt,int mgmt_group_cipher,struct wpa_sm * sm)12939beb93cSSam Leffler static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
13039beb93cSSam Leffler 			      int pairwise_cipher, int group_cipher,
13139beb93cSSam Leffler 			      int key_mgmt, int mgmt_group_cipher,
13239beb93cSSam Leffler 			      struct wpa_sm *sm)
13339beb93cSSam Leffler {
13439beb93cSSam Leffler 	u8 *pos;
13539beb93cSSam Leffler 	struct rsn_ie_hdr *hdr;
136f05cddf9SRui Paulo 	u32 suite;
13739beb93cSSam Leffler 
13839beb93cSSam Leffler 	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
13939beb93cSSam Leffler 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
14039beb93cSSam Leffler 	    (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
14139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
14239beb93cSSam Leffler 			   (unsigned long) rsn_ie_len);
14339beb93cSSam Leffler 		return -1;
14439beb93cSSam Leffler 	}
14539beb93cSSam Leffler 
14639beb93cSSam Leffler 	hdr = (struct rsn_ie_hdr *) rsn_ie;
14739beb93cSSam Leffler 	hdr->elem_id = WLAN_EID_RSN;
14839beb93cSSam Leffler 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
14939beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
15039beb93cSSam Leffler 
151f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
152f05cddf9SRui Paulo 	if (suite == 0) {
15339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
15439beb93cSSam Leffler 			   group_cipher);
15539beb93cSSam Leffler 		return -1;
15639beb93cSSam Leffler 	}
157f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
15839beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
15939beb93cSSam Leffler 
16039beb93cSSam Leffler 	*pos++ = 1;
16139beb93cSSam Leffler 	*pos++ = 0;
162f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
163f05cddf9SRui Paulo 	if (suite == 0 ||
164f05cddf9SRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
165f05cddf9SRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
16639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
16739beb93cSSam Leffler 			   pairwise_cipher);
16839beb93cSSam Leffler 		return -1;
16939beb93cSSam Leffler 	}
170f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
17139beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
17239beb93cSSam Leffler 
17339beb93cSSam Leffler 	*pos++ = 1;
17439beb93cSSam Leffler 	*pos++ = 0;
17539beb93cSSam Leffler 	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
17639beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
17739beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
17839beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
179f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
180f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
18139beb93cSSam Leffler #ifdef CONFIG_IEEE80211R
18239beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
18339beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
18485732ac8SCy Schubert #ifdef CONFIG_SHA384
18585732ac8SCy Schubert 	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
18685732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
18785732ac8SCy Schubert #endif /* CONFIG_SHA384 */
18839beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
18939beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
19039beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */
19139beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
19239beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
19339beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
19439beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
195f05cddf9SRui Paulo #ifdef CONFIG_SAE
196f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
197f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
198*a90b9d01SCy Schubert 	} else if (key_mgmt == WPA_KEY_MGMT_SAE_EXT_KEY) {
199*a90b9d01SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
200f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
201f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
202*a90b9d01SCy Schubert 	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
203*a90b9d01SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
204f05cddf9SRui Paulo #endif /* CONFIG_SAE */
2055b9c547cSRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
2065b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
2075b9c547cSRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
2085b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
20985732ac8SCy Schubert #ifdef CONFIG_FILS
21085732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
21185732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
21285732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
21385732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
21485732ac8SCy Schubert #ifdef CONFIG_IEEE80211R
21585732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
21685732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
21785732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
21885732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
21985732ac8SCy Schubert #endif /* CONFIG_IEEE80211R */
22085732ac8SCy Schubert #endif /* CONFIG_FILS */
22185732ac8SCy Schubert #ifdef CONFIG_OWE
22285732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_OWE) {
22385732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
22485732ac8SCy Schubert #endif /* CONFIG_OWE */
22585732ac8SCy Schubert #ifdef CONFIG_DPP
22685732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_DPP) {
22785732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
22885732ac8SCy Schubert #endif /* CONFIG_DPP */
22985732ac8SCy Schubert #ifdef CONFIG_HS20
23085732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_OSEN) {
23185732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
23285732ac8SCy Schubert #endif /* CONFIG_HS20 */
233*a90b9d01SCy Schubert #ifdef CONFIG_SHA384
234*a90b9d01SCy Schubert 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA384) {
235*a90b9d01SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
236*a90b9d01SCy Schubert #endif /* CONFIG_SHA384 */
23739beb93cSSam Leffler 	} else {
23839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
23939beb93cSSam Leffler 			   key_mgmt);
24039beb93cSSam Leffler 		return -1;
24139beb93cSSam Leffler 	}
24239beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
24339beb93cSSam Leffler 
24439beb93cSSam Leffler 	/* RSN Capabilities */
245c1d255d3SCy Schubert 	WPA_PUT_LE16(pos, rsn_supp_capab(sm));
24639beb93cSSam Leffler 	pos += 2;
24739beb93cSSam Leffler 
24839beb93cSSam Leffler 	if (sm->cur_pmksa) {
24939beb93cSSam Leffler 		/* PMKID Count (2 octets, little endian) */
25039beb93cSSam Leffler 		*pos++ = 1;
25139beb93cSSam Leffler 		*pos++ = 0;
25239beb93cSSam Leffler 		/* PMKID */
25339beb93cSSam Leffler 		os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
25439beb93cSSam Leffler 		pos += PMKID_LEN;
25539beb93cSSam Leffler 	}
25639beb93cSSam Leffler 
2575b9c547cSRui Paulo 	if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
25839beb93cSSam Leffler 		if (!sm->cur_pmksa) {
25939beb93cSSam Leffler 			/* PMKID Count */
26039beb93cSSam Leffler 			WPA_PUT_LE16(pos, 0);
26139beb93cSSam Leffler 			pos += 2;
26239beb93cSSam Leffler 		}
26339beb93cSSam Leffler 
26439beb93cSSam Leffler 		/* Management Group Cipher Suite */
2655b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
2665b9c547cSRui Paulo 							  mgmt_group_cipher));
26739beb93cSSam Leffler 		pos += RSN_SELECTOR_LEN;
26839beb93cSSam Leffler 	}
26939beb93cSSam Leffler 
27039beb93cSSam Leffler 	hdr->len = (pos - rsn_ie) - 2;
27139beb93cSSam Leffler 
27239beb93cSSam Leffler 	WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
27339beb93cSSam Leffler 
27439beb93cSSam Leffler 	return pos - rsn_ie;
27539beb93cSSam Leffler }
27639beb93cSSam Leffler 
27739beb93cSSam Leffler 
2785b9c547cSRui Paulo #ifdef CONFIG_HS20
wpa_gen_wpa_ie_osen(u8 * wpa_ie,size_t wpa_ie_len,int pairwise_cipher,int group_cipher,int key_mgmt)2795b9c547cSRui Paulo static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
2805b9c547cSRui Paulo 			       int pairwise_cipher, int group_cipher,
2815b9c547cSRui Paulo 			       int key_mgmt)
2825b9c547cSRui Paulo {
2835b9c547cSRui Paulo 	u8 *pos, *len;
2845b9c547cSRui Paulo 	u32 suite;
2855b9c547cSRui Paulo 
2865b9c547cSRui Paulo 	if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
2875b9c547cSRui Paulo 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
2885b9c547cSRui Paulo 		return -1;
2895b9c547cSRui Paulo 
2905b9c547cSRui Paulo 	pos = wpa_ie;
2915b9c547cSRui Paulo 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
2925b9c547cSRui Paulo 	len = pos++; /* to be filled */
2935b9c547cSRui Paulo 	WPA_PUT_BE24(pos, OUI_WFA);
2945b9c547cSRui Paulo 	pos += 3;
2955b9c547cSRui Paulo 	*pos++ = HS20_OSEN_OUI_TYPE;
2965b9c547cSRui Paulo 
2975b9c547cSRui Paulo 	/* Group Data Cipher Suite */
2985b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
2995b9c547cSRui Paulo 	if (suite == 0) {
3005b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
3015b9c547cSRui Paulo 			   group_cipher);
3025b9c547cSRui Paulo 		return -1;
3035b9c547cSRui Paulo 	}
3045b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
3055b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
3065b9c547cSRui Paulo 
3075b9c547cSRui Paulo 	/* Pairwise Cipher Suite Count and List */
3085b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
3095b9c547cSRui Paulo 	pos += 2;
3105b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
3115b9c547cSRui Paulo 	if (suite == 0 ||
3125b9c547cSRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
3135b9c547cSRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
3145b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
3155b9c547cSRui Paulo 			   pairwise_cipher);
3165b9c547cSRui Paulo 		return -1;
3175b9c547cSRui Paulo 	}
3185b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
3195b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
3205b9c547cSRui Paulo 
3215b9c547cSRui Paulo 	/* AKM Suite Count and List */
3225b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
3235b9c547cSRui Paulo 	pos += 2;
3245b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
3255b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
3265b9c547cSRui Paulo 
3275b9c547cSRui Paulo 	*len = pos - len - 1;
3285b9c547cSRui Paulo 
3295b9c547cSRui Paulo 	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
3305b9c547cSRui Paulo 
3315b9c547cSRui Paulo 	return pos - wpa_ie;
3325b9c547cSRui Paulo }
3335b9c547cSRui Paulo #endif /* CONFIG_HS20 */
3345b9c547cSRui Paulo 
3355b9c547cSRui Paulo 
33639beb93cSSam Leffler /**
33739beb93cSSam Leffler  * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
33839beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
33939beb93cSSam Leffler  * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
34039beb93cSSam Leffler  * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
34139beb93cSSam Leffler  * Returns: Length of the generated WPA/RSN IE or -1 on failure
34239beb93cSSam Leffler  */
wpa_gen_wpa_ie(struct wpa_sm * sm,u8 * wpa_ie,size_t wpa_ie_len)34339beb93cSSam Leffler int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
34439beb93cSSam Leffler {
34539beb93cSSam Leffler 	if (sm->proto == WPA_PROTO_RSN)
34639beb93cSSam Leffler 		return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
34739beb93cSSam Leffler 					  sm->pairwise_cipher,
34839beb93cSSam Leffler 					  sm->group_cipher,
34939beb93cSSam Leffler 					  sm->key_mgmt, sm->mgmt_group_cipher,
35039beb93cSSam Leffler 					  sm);
3515b9c547cSRui Paulo #ifdef CONFIG_HS20
3525b9c547cSRui Paulo 	else if (sm->proto == WPA_PROTO_OSEN)
3535b9c547cSRui Paulo 		return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
3545b9c547cSRui Paulo 					   sm->pairwise_cipher,
3555b9c547cSRui Paulo 					   sm->group_cipher,
3565b9c547cSRui Paulo 					   sm->key_mgmt);
3575b9c547cSRui Paulo #endif /* CONFIG_HS20 */
35839beb93cSSam Leffler 	else
35939beb93cSSam Leffler 		return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
36039beb93cSSam Leffler 					  sm->pairwise_cipher,
36139beb93cSSam Leffler 					  sm->group_cipher,
36239beb93cSSam Leffler 					  sm->key_mgmt);
36339beb93cSSam Leffler }
36439beb93cSSam Leffler 
36539beb93cSSam Leffler 
wpa_gen_rsnxe(struct wpa_sm * sm,u8 * rsnxe,size_t rsnxe_len)366c1d255d3SCy Schubert int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
3675b9c547cSRui Paulo {
368c1d255d3SCy Schubert 	u8 *pos = rsnxe;
369*a90b9d01SCy Schubert 	u32 capab = 0, tmp;
370c1d255d3SCy Schubert 	size_t flen;
3715b9c547cSRui Paulo 
372c1d255d3SCy Schubert 	if (wpa_key_mgmt_sae(sm->key_mgmt) &&
373*a90b9d01SCy Schubert 	    (sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
374*a90b9d01SCy Schubert 	     sm->sae_pwe == SAE_PWE_BOTH || sm->sae_pk)) {
375c1d255d3SCy Schubert 		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
376c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
377c1d255d3SCy Schubert 		if (sm->sae_pk)
378c1d255d3SCy Schubert 			capab |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
379c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */
3805b9c547cSRui Paulo 	}
3815b9c547cSRui Paulo 
382c1d255d3SCy Schubert 	if (sm->secure_ltf)
383c1d255d3SCy Schubert 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
384c1d255d3SCy Schubert 	if (sm->secure_rtt)
385c1d255d3SCy Schubert 		capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
386c1d255d3SCy Schubert 	if (sm->prot_range_neg)
387*a90b9d01SCy Schubert 		capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
388*a90b9d01SCy Schubert 	if (sm->ssid_protection)
389*a90b9d01SCy Schubert 		capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
3905b9c547cSRui Paulo 
391c1d255d3SCy Schubert 	if (!capab)
392c1d255d3SCy Schubert 		return 0; /* no supported extended RSN capabilities */
393*a90b9d01SCy Schubert 	tmp = capab;
394*a90b9d01SCy Schubert 	flen = 0;
395*a90b9d01SCy Schubert 	while (tmp) {
396*a90b9d01SCy Schubert 		flen++;
397*a90b9d01SCy Schubert 		tmp >>= 8;
398*a90b9d01SCy Schubert 	}
399c1d255d3SCy Schubert 	if (rsnxe_len < 2 + flen)
400c1d255d3SCy Schubert 		return -1;
401c1d255d3SCy Schubert 	capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
4025b9c547cSRui Paulo 
403c1d255d3SCy Schubert 	*pos++ = WLAN_EID_RSNX;
404c1d255d3SCy Schubert 	*pos++ = flen;
405*a90b9d01SCy Schubert 	while (capab) {
406*a90b9d01SCy Schubert 		*pos++ = capab & 0xff;
407c1d255d3SCy Schubert 		capab >>= 8;
408*a90b9d01SCy Schubert 	}
40939beb93cSSam Leffler 
410c1d255d3SCy Schubert 	return pos - rsnxe;
41139beb93cSSam Leffler }
412