xref: /freebsd/contrib/wpa/src/wps/wps_enrollee.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * Wi-Fi Protected Setup - Enrollee
339beb93cSSam Leffler  * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5*f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6*f05cddf9SRui 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/crypto.h"
13e28a4053SRui Paulo #include "crypto/sha256.h"
14*f05cddf9SRui Paulo #include "crypto/random.h"
1539beb93cSSam Leffler #include "wps_i.h"
1639beb93cSSam Leffler #include "wps_dev_attr.h"
1739beb93cSSam Leffler 
1839beb93cSSam Leffler 
1939beb93cSSam Leffler static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
2039beb93cSSam Leffler {
2139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address");
2239beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
2339beb93cSSam Leffler 	wpabuf_put_be16(msg, ETH_ALEN);
2439beb93cSSam Leffler 	wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
2539beb93cSSam Leffler 	return 0;
2639beb93cSSam Leffler }
2739beb93cSSam Leffler 
2839beb93cSSam Leffler 
2939beb93cSSam Leffler static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
3039beb93cSSam Leffler {
3139beb93cSSam Leffler 	u8 state;
3239beb93cSSam Leffler 	if (wps->wps->ap)
3339beb93cSSam Leffler 		state = wps->wps->wps_state;
3439beb93cSSam Leffler 	else
3539beb93cSSam Leffler 		state = WPS_STATE_NOT_CONFIGURED;
3639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
3739beb93cSSam Leffler 		   state);
3839beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_WPS_STATE);
3939beb93cSSam Leffler 	wpabuf_put_be16(msg, 1);
403157ba21SRui Paulo 	wpabuf_put_u8(msg, state);
4139beb93cSSam Leffler 	return 0;
4239beb93cSSam Leffler }
4339beb93cSSam Leffler 
4439beb93cSSam Leffler 
4539beb93cSSam Leffler static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
4639beb93cSSam Leffler {
4739beb93cSSam Leffler 	u8 *hash;
4839beb93cSSam Leffler 	const u8 *addr[4];
4939beb93cSSam Leffler 	size_t len[4];
5039beb93cSSam Leffler 
51*f05cddf9SRui Paulo 	if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
5239beb93cSSam Leffler 		return -1;
5339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
5439beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
5539beb93cSSam Leffler 		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
5639beb93cSSam Leffler 
5739beb93cSSam Leffler 	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
5839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
5939beb93cSSam Leffler 			   "E-Hash derivation");
6039beb93cSSam Leffler 		return -1;
6139beb93cSSam Leffler 	}
6239beb93cSSam Leffler 
6339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
6439beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_HASH1);
6539beb93cSSam Leffler 	wpabuf_put_be16(msg, SHA256_MAC_LEN);
6639beb93cSSam Leffler 	hash = wpabuf_put(msg, SHA256_MAC_LEN);
6739beb93cSSam Leffler 	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
6839beb93cSSam Leffler 	addr[0] = wps->snonce;
6939beb93cSSam Leffler 	len[0] = WPS_SECRET_NONCE_LEN;
7039beb93cSSam Leffler 	addr[1] = wps->psk1;
7139beb93cSSam Leffler 	len[1] = WPS_PSK_LEN;
7239beb93cSSam Leffler 	addr[2] = wpabuf_head(wps->dh_pubkey_e);
7339beb93cSSam Leffler 	len[2] = wpabuf_len(wps->dh_pubkey_e);
7439beb93cSSam Leffler 	addr[3] = wpabuf_head(wps->dh_pubkey_r);
7539beb93cSSam Leffler 	len[3] = wpabuf_len(wps->dh_pubkey_r);
7639beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
7739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
7839beb93cSSam Leffler 
7939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
8039beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_HASH2);
8139beb93cSSam Leffler 	wpabuf_put_be16(msg, SHA256_MAC_LEN);
8239beb93cSSam Leffler 	hash = wpabuf_put(msg, SHA256_MAC_LEN);
8339beb93cSSam Leffler 	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
8439beb93cSSam Leffler 	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
8539beb93cSSam Leffler 	addr[1] = wps->psk2;
8639beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
8739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
8839beb93cSSam Leffler 
8939beb93cSSam Leffler 	return 0;
9039beb93cSSam Leffler }
9139beb93cSSam Leffler 
9239beb93cSSam Leffler 
9339beb93cSSam Leffler static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
9439beb93cSSam Leffler {
9539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
9639beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_SNONCE1);
9739beb93cSSam Leffler 	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
9839beb93cSSam Leffler 	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
9939beb93cSSam Leffler 	return 0;
10039beb93cSSam Leffler }
10139beb93cSSam Leffler 
10239beb93cSSam Leffler 
10339beb93cSSam Leffler static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
10439beb93cSSam Leffler {
10539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
10639beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_SNONCE2);
10739beb93cSSam Leffler 	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
10839beb93cSSam Leffler 	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
10939beb93cSSam Leffler 			WPS_SECRET_NONCE_LEN);
11039beb93cSSam Leffler 	return 0;
11139beb93cSSam Leffler }
11239beb93cSSam Leffler 
11339beb93cSSam Leffler 
11439beb93cSSam Leffler static struct wpabuf * wps_build_m1(struct wps_data *wps)
11539beb93cSSam Leffler {
11639beb93cSSam Leffler 	struct wpabuf *msg;
117*f05cddf9SRui Paulo 	u16 config_methods;
11839beb93cSSam Leffler 
119*f05cddf9SRui Paulo 	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
12039beb93cSSam Leffler 		return NULL;
12139beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
12239beb93cSSam Leffler 		    wps->nonce_e, WPS_NONCE_LEN);
12339beb93cSSam Leffler 
12439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
12539beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
12639beb93cSSam Leffler 	if (msg == NULL)
12739beb93cSSam Leffler 		return NULL;
12839beb93cSSam Leffler 
129*f05cddf9SRui Paulo 	config_methods = wps->wps->config_methods;
130*f05cddf9SRui Paulo 	if (wps->wps->ap && !wps->pbc_in_m1 &&
131*f05cddf9SRui Paulo 	    (wps->dev_password_len != 0 ||
132*f05cddf9SRui Paulo 	     (config_methods & WPS_CONFIG_DISPLAY))) {
133*f05cddf9SRui Paulo 		/*
134*f05cddf9SRui Paulo 		 * These are the methods that the AP supports as an Enrollee
135*f05cddf9SRui Paulo 		 * for adding external Registrars, so remove PushButton.
136*f05cddf9SRui Paulo 		 *
137*f05cddf9SRui Paulo 		 * As a workaround for Windows 7 mechanism for probing WPS
138*f05cddf9SRui Paulo 		 * capabilities from M1, leave PushButton option if no PIN
139*f05cddf9SRui Paulo 		 * method is available or if WPS configuration enables PBC
140*f05cddf9SRui Paulo 		 * workaround.
141*f05cddf9SRui Paulo 		 */
142*f05cddf9SRui Paulo 		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
143*f05cddf9SRui Paulo #ifdef CONFIG_WPS2
144*f05cddf9SRui Paulo 		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
145*f05cddf9SRui Paulo 				    WPS_CONFIG_PHY_PUSHBUTTON);
146*f05cddf9SRui Paulo #endif /* CONFIG_WPS2 */
147*f05cddf9SRui Paulo 	}
148*f05cddf9SRui Paulo 
14939beb93cSSam Leffler 	if (wps_build_version(msg) ||
15039beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M1) ||
15139beb93cSSam Leffler 	    wps_build_uuid_e(msg, wps->uuid_e) ||
15239beb93cSSam Leffler 	    wps_build_mac_addr(wps, msg) ||
15339beb93cSSam Leffler 	    wps_build_enrollee_nonce(wps, msg) ||
15439beb93cSSam Leffler 	    wps_build_public_key(wps, msg) ||
15539beb93cSSam Leffler 	    wps_build_auth_type_flags(wps, msg) ||
15639beb93cSSam Leffler 	    wps_build_encr_type_flags(wps, msg) ||
15739beb93cSSam Leffler 	    wps_build_conn_type_flags(wps, msg) ||
158*f05cddf9SRui Paulo 	    wps_build_config_methods(msg, config_methods) ||
15939beb93cSSam Leffler 	    wps_build_wps_state(wps, msg) ||
16039beb93cSSam Leffler 	    wps_build_device_attrs(&wps->wps->dev, msg) ||
16139beb93cSSam Leffler 	    wps_build_rf_bands(&wps->wps->dev, msg) ||
16239beb93cSSam Leffler 	    wps_build_assoc_state(wps, msg) ||
16339beb93cSSam Leffler 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
16439beb93cSSam Leffler 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
165*f05cddf9SRui Paulo 	    wps_build_os_version(&wps->wps->dev, msg) ||
166*f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
167*f05cddf9SRui Paulo 	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
16839beb93cSSam Leffler 		wpabuf_free(msg);
16939beb93cSSam Leffler 		return NULL;
17039beb93cSSam Leffler 	}
17139beb93cSSam Leffler 
17239beb93cSSam Leffler 	wps->state = RECV_M2;
17339beb93cSSam Leffler 	return msg;
17439beb93cSSam Leffler }
17539beb93cSSam Leffler 
17639beb93cSSam Leffler 
17739beb93cSSam Leffler static struct wpabuf * wps_build_m3(struct wps_data *wps)
17839beb93cSSam Leffler {
17939beb93cSSam Leffler 	struct wpabuf *msg;
18039beb93cSSam Leffler 
18139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
18239beb93cSSam Leffler 
18339beb93cSSam Leffler 	if (wps->dev_password == NULL) {
18439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
18539beb93cSSam Leffler 		return NULL;
18639beb93cSSam Leffler 	}
18739beb93cSSam Leffler 	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
18839beb93cSSam Leffler 
18939beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
19039beb93cSSam Leffler 	if (msg == NULL)
19139beb93cSSam Leffler 		return NULL;
19239beb93cSSam Leffler 
19339beb93cSSam Leffler 	if (wps_build_version(msg) ||
19439beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M3) ||
19539beb93cSSam Leffler 	    wps_build_registrar_nonce(wps, msg) ||
19639beb93cSSam Leffler 	    wps_build_e_hash(wps, msg) ||
197*f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
19839beb93cSSam Leffler 	    wps_build_authenticator(wps, msg)) {
19939beb93cSSam Leffler 		wpabuf_free(msg);
20039beb93cSSam Leffler 		return NULL;
20139beb93cSSam Leffler 	}
20239beb93cSSam Leffler 
20339beb93cSSam Leffler 	wps->state = RECV_M4;
20439beb93cSSam Leffler 	return msg;
20539beb93cSSam Leffler }
20639beb93cSSam Leffler 
20739beb93cSSam Leffler 
20839beb93cSSam Leffler static struct wpabuf * wps_build_m5(struct wps_data *wps)
20939beb93cSSam Leffler {
21039beb93cSSam Leffler 	struct wpabuf *msg, *plain;
21139beb93cSSam Leffler 
21239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
21339beb93cSSam Leffler 
21439beb93cSSam Leffler 	plain = wpabuf_alloc(200);
21539beb93cSSam Leffler 	if (plain == NULL)
21639beb93cSSam Leffler 		return NULL;
21739beb93cSSam Leffler 
21839beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
21939beb93cSSam Leffler 	if (msg == NULL) {
22039beb93cSSam Leffler 		wpabuf_free(plain);
22139beb93cSSam Leffler 		return NULL;
22239beb93cSSam Leffler 	}
22339beb93cSSam Leffler 
22439beb93cSSam Leffler 	if (wps_build_version(msg) ||
22539beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M5) ||
22639beb93cSSam Leffler 	    wps_build_registrar_nonce(wps, msg) ||
22739beb93cSSam Leffler 	    wps_build_e_snonce1(wps, plain) ||
22839beb93cSSam Leffler 	    wps_build_key_wrap_auth(wps, plain) ||
22939beb93cSSam Leffler 	    wps_build_encr_settings(wps, msg, plain) ||
230*f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
23139beb93cSSam Leffler 	    wps_build_authenticator(wps, msg)) {
23239beb93cSSam Leffler 		wpabuf_free(plain);
23339beb93cSSam Leffler 		wpabuf_free(msg);
23439beb93cSSam Leffler 		return NULL;
23539beb93cSSam Leffler 	}
23639beb93cSSam Leffler 	wpabuf_free(plain);
23739beb93cSSam Leffler 
23839beb93cSSam Leffler 	wps->state = RECV_M6;
23939beb93cSSam Leffler 	return msg;
24039beb93cSSam Leffler }
24139beb93cSSam Leffler 
24239beb93cSSam Leffler 
24339beb93cSSam Leffler static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
24439beb93cSSam Leffler {
24539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
24639beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_SSID);
24739beb93cSSam Leffler 	wpabuf_put_be16(msg, wps->wps->ssid_len);
24839beb93cSSam Leffler 	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
24939beb93cSSam Leffler 	return 0;
25039beb93cSSam Leffler }
25139beb93cSSam Leffler 
25239beb93cSSam Leffler 
25339beb93cSSam Leffler static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
25439beb93cSSam Leffler {
255*f05cddf9SRui Paulo 	u16 auth_type = wps->wps->auth_types;
256*f05cddf9SRui Paulo 
257*f05cddf9SRui Paulo 	/* Select the best authentication type */
258*f05cddf9SRui Paulo 	if (auth_type & WPS_AUTH_WPA2PSK)
259*f05cddf9SRui Paulo 		auth_type = WPS_AUTH_WPA2PSK;
260*f05cddf9SRui Paulo 	else if (auth_type & WPS_AUTH_WPAPSK)
261*f05cddf9SRui Paulo 		auth_type = WPS_AUTH_WPAPSK;
262*f05cddf9SRui Paulo 	else if (auth_type & WPS_AUTH_OPEN)
263*f05cddf9SRui Paulo 		auth_type = WPS_AUTH_OPEN;
264*f05cddf9SRui Paulo 	else if (auth_type & WPS_AUTH_SHARED)
265*f05cddf9SRui Paulo 		auth_type = WPS_AUTH_SHARED;
266*f05cddf9SRui Paulo 
267*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
26839beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
26939beb93cSSam Leffler 	wpabuf_put_be16(msg, 2);
270*f05cddf9SRui Paulo 	wpabuf_put_be16(msg, auth_type);
27139beb93cSSam Leffler 	return 0;
27239beb93cSSam Leffler }
27339beb93cSSam Leffler 
27439beb93cSSam Leffler 
27539beb93cSSam Leffler static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
27639beb93cSSam Leffler {
277*f05cddf9SRui Paulo 	u16 encr_type = wps->wps->encr_types;
278*f05cddf9SRui Paulo 
279*f05cddf9SRui Paulo 	/* Select the best encryption type */
280*f05cddf9SRui Paulo 	if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
281*f05cddf9SRui Paulo 		if (encr_type & WPS_ENCR_AES)
282*f05cddf9SRui Paulo 			encr_type = WPS_ENCR_AES;
283*f05cddf9SRui Paulo 		else if (encr_type & WPS_ENCR_TKIP)
284*f05cddf9SRui Paulo 			encr_type = WPS_ENCR_TKIP;
285*f05cddf9SRui Paulo 	} else {
286*f05cddf9SRui Paulo 		if (encr_type & WPS_ENCR_WEP)
287*f05cddf9SRui Paulo 			encr_type = WPS_ENCR_WEP;
288*f05cddf9SRui Paulo 		else if (encr_type & WPS_ENCR_NONE)
289*f05cddf9SRui Paulo 			encr_type = WPS_ENCR_NONE;
290*f05cddf9SRui Paulo 	}
291*f05cddf9SRui Paulo 
292*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
29339beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
29439beb93cSSam Leffler 	wpabuf_put_be16(msg, 2);
295*f05cddf9SRui Paulo 	wpabuf_put_be16(msg, encr_type);
29639beb93cSSam Leffler 	return 0;
29739beb93cSSam Leffler }
29839beb93cSSam Leffler 
29939beb93cSSam Leffler 
30039beb93cSSam Leffler static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
30139beb93cSSam Leffler {
30239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
30339beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
30439beb93cSSam Leffler 	wpabuf_put_be16(msg, wps->wps->network_key_len);
30539beb93cSSam Leffler 	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
30639beb93cSSam Leffler 	return 0;
30739beb93cSSam Leffler }
30839beb93cSSam Leffler 
30939beb93cSSam Leffler 
31039beb93cSSam Leffler static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
31139beb93cSSam Leffler {
31239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
31339beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
31439beb93cSSam Leffler 	wpabuf_put_be16(msg, ETH_ALEN);
31539beb93cSSam Leffler 	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
31639beb93cSSam Leffler 	return 0;
31739beb93cSSam Leffler }
31839beb93cSSam Leffler 
31939beb93cSSam Leffler 
32039beb93cSSam Leffler static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
32139beb93cSSam Leffler {
32239beb93cSSam Leffler 	if (wps->wps->ap_settings) {
32339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
32439beb93cSSam Leffler 		wpabuf_put_data(plain, wps->wps->ap_settings,
32539beb93cSSam Leffler 				wps->wps->ap_settings_len);
32639beb93cSSam Leffler 		return 0;
32739beb93cSSam Leffler 	}
32839beb93cSSam Leffler 
32939beb93cSSam Leffler 	return wps_build_cred_ssid(wps, plain) ||
33039beb93cSSam Leffler 		wps_build_cred_mac_addr(wps, plain) ||
33139beb93cSSam Leffler 		wps_build_cred_auth_type(wps, plain) ||
33239beb93cSSam Leffler 		wps_build_cred_encr_type(wps, plain) ||
33339beb93cSSam Leffler 		wps_build_cred_network_key(wps, plain);
33439beb93cSSam Leffler }
33539beb93cSSam Leffler 
33639beb93cSSam Leffler 
33739beb93cSSam Leffler static struct wpabuf * wps_build_m7(struct wps_data *wps)
33839beb93cSSam Leffler {
33939beb93cSSam Leffler 	struct wpabuf *msg, *plain;
34039beb93cSSam Leffler 
34139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
34239beb93cSSam Leffler 
34339beb93cSSam Leffler 	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
34439beb93cSSam Leffler 	if (plain == NULL)
34539beb93cSSam Leffler 		return NULL;
34639beb93cSSam Leffler 
34739beb93cSSam Leffler 	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
34839beb93cSSam Leffler 	if (msg == NULL) {
34939beb93cSSam Leffler 		wpabuf_free(plain);
35039beb93cSSam Leffler 		return NULL;
35139beb93cSSam Leffler 	}
35239beb93cSSam Leffler 
35339beb93cSSam Leffler 	if (wps_build_version(msg) ||
35439beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M7) ||
35539beb93cSSam Leffler 	    wps_build_registrar_nonce(wps, msg) ||
35639beb93cSSam Leffler 	    wps_build_e_snonce2(wps, plain) ||
35739beb93cSSam Leffler 	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
35839beb93cSSam Leffler 	    wps_build_key_wrap_auth(wps, plain) ||
35939beb93cSSam Leffler 	    wps_build_encr_settings(wps, msg, plain) ||
360*f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
36139beb93cSSam Leffler 	    wps_build_authenticator(wps, msg)) {
36239beb93cSSam Leffler 		wpabuf_free(plain);
36339beb93cSSam Leffler 		wpabuf_free(msg);
36439beb93cSSam Leffler 		return NULL;
36539beb93cSSam Leffler 	}
36639beb93cSSam Leffler 	wpabuf_free(plain);
36739beb93cSSam Leffler 
368e28a4053SRui Paulo 	if (wps->wps->ap && wps->wps->registrar) {
369e28a4053SRui Paulo 		/*
370e28a4053SRui Paulo 		 * If the Registrar is only learning our current configuration,
371e28a4053SRui Paulo 		 * it may not continue protocol run to successful completion.
372e28a4053SRui Paulo 		 * Store information here to make sure it remains available.
373e28a4053SRui Paulo 		 */
374e28a4053SRui Paulo 		wps_device_store(wps->wps->registrar, &wps->peer_dev,
375e28a4053SRui Paulo 				 wps->uuid_r);
376e28a4053SRui Paulo 	}
377e28a4053SRui Paulo 
37839beb93cSSam Leffler 	wps->state = RECV_M8;
37939beb93cSSam Leffler 	return msg;
38039beb93cSSam Leffler }
38139beb93cSSam Leffler 
38239beb93cSSam Leffler 
38339beb93cSSam Leffler static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
38439beb93cSSam Leffler {
38539beb93cSSam Leffler 	struct wpabuf *msg;
38639beb93cSSam Leffler 
38739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
38839beb93cSSam Leffler 
38939beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
39039beb93cSSam Leffler 	if (msg == NULL)
39139beb93cSSam Leffler 		return NULL;
39239beb93cSSam Leffler 
39339beb93cSSam Leffler 	if (wps_build_version(msg) ||
39439beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
39539beb93cSSam Leffler 	    wps_build_enrollee_nonce(wps, msg) ||
396*f05cddf9SRui Paulo 	    wps_build_registrar_nonce(wps, msg) ||
397*f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
39839beb93cSSam Leffler 		wpabuf_free(msg);
39939beb93cSSam Leffler 		return NULL;
40039beb93cSSam Leffler 	}
40139beb93cSSam Leffler 
40239beb93cSSam Leffler 	if (wps->wps->ap)
40339beb93cSSam Leffler 		wps->state = RECV_ACK;
40439beb93cSSam Leffler 	else {
40539beb93cSSam Leffler 		wps_success_event(wps->wps);
40639beb93cSSam Leffler 		wps->state = WPS_FINISHED;
40739beb93cSSam Leffler 	}
40839beb93cSSam Leffler 	return msg;
40939beb93cSSam Leffler }
41039beb93cSSam Leffler 
41139beb93cSSam Leffler 
41239beb93cSSam Leffler struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
41339beb93cSSam Leffler 				     enum wsc_op_code *op_code)
41439beb93cSSam Leffler {
41539beb93cSSam Leffler 	struct wpabuf *msg;
41639beb93cSSam Leffler 
41739beb93cSSam Leffler 	switch (wps->state) {
41839beb93cSSam Leffler 	case SEND_M1:
41939beb93cSSam Leffler 		msg = wps_build_m1(wps);
42039beb93cSSam Leffler 		*op_code = WSC_MSG;
42139beb93cSSam Leffler 		break;
42239beb93cSSam Leffler 	case SEND_M3:
42339beb93cSSam Leffler 		msg = wps_build_m3(wps);
42439beb93cSSam Leffler 		*op_code = WSC_MSG;
42539beb93cSSam Leffler 		break;
42639beb93cSSam Leffler 	case SEND_M5:
42739beb93cSSam Leffler 		msg = wps_build_m5(wps);
42839beb93cSSam Leffler 		*op_code = WSC_MSG;
42939beb93cSSam Leffler 		break;
43039beb93cSSam Leffler 	case SEND_M7:
43139beb93cSSam Leffler 		msg = wps_build_m7(wps);
43239beb93cSSam Leffler 		*op_code = WSC_MSG;
43339beb93cSSam Leffler 		break;
43439beb93cSSam Leffler 	case RECEIVED_M2D:
43539beb93cSSam Leffler 		if (wps->wps->ap) {
43639beb93cSSam Leffler 			msg = wps_build_wsc_nack(wps);
43739beb93cSSam Leffler 			*op_code = WSC_NACK;
43839beb93cSSam Leffler 			break;
43939beb93cSSam Leffler 		}
44039beb93cSSam Leffler 		msg = wps_build_wsc_ack(wps);
44139beb93cSSam Leffler 		*op_code = WSC_ACK;
44239beb93cSSam Leffler 		if (msg) {
44339beb93cSSam Leffler 			/* Another M2/M2D may be received */
44439beb93cSSam Leffler 			wps->state = RECV_M2;
44539beb93cSSam Leffler 		}
44639beb93cSSam Leffler 		break;
44739beb93cSSam Leffler 	case SEND_WSC_NACK:
44839beb93cSSam Leffler 		msg = wps_build_wsc_nack(wps);
44939beb93cSSam Leffler 		*op_code = WSC_NACK;
45039beb93cSSam Leffler 		break;
45139beb93cSSam Leffler 	case WPS_MSG_DONE:
45239beb93cSSam Leffler 		msg = wps_build_wsc_done(wps);
45339beb93cSSam Leffler 		*op_code = WSC_Done;
45439beb93cSSam Leffler 		break;
45539beb93cSSam Leffler 	default:
45639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
45739beb93cSSam Leffler 			   "a message", wps->state);
45839beb93cSSam Leffler 		msg = NULL;
45939beb93cSSam Leffler 		break;
46039beb93cSSam Leffler 	}
46139beb93cSSam Leffler 
46239beb93cSSam Leffler 	if (*op_code == WSC_MSG && msg) {
46339beb93cSSam Leffler 		/* Save a copy of the last message for Authenticator derivation
46439beb93cSSam Leffler 		 */
46539beb93cSSam Leffler 		wpabuf_free(wps->last_msg);
46639beb93cSSam Leffler 		wps->last_msg = wpabuf_dup(msg);
46739beb93cSSam Leffler 	}
46839beb93cSSam Leffler 
46939beb93cSSam Leffler 	return msg;
47039beb93cSSam Leffler }
47139beb93cSSam Leffler 
47239beb93cSSam Leffler 
47339beb93cSSam Leffler static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
47439beb93cSSam Leffler {
47539beb93cSSam Leffler 	if (r_nonce == NULL) {
47639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
47739beb93cSSam Leffler 		return -1;
47839beb93cSSam Leffler 	}
47939beb93cSSam Leffler 
48039beb93cSSam Leffler 	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
48139beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
48239beb93cSSam Leffler 		    wps->nonce_r, WPS_NONCE_LEN);
48339beb93cSSam Leffler 
48439beb93cSSam Leffler 	return 0;
48539beb93cSSam Leffler }
48639beb93cSSam Leffler 
48739beb93cSSam Leffler 
48839beb93cSSam Leffler static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
48939beb93cSSam Leffler {
49039beb93cSSam Leffler 	if (e_nonce == NULL) {
49139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
49239beb93cSSam Leffler 		return -1;
49339beb93cSSam Leffler 	}
49439beb93cSSam Leffler 
49539beb93cSSam Leffler 	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
49639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
49739beb93cSSam Leffler 		return -1;
49839beb93cSSam Leffler 	}
49939beb93cSSam Leffler 
50039beb93cSSam Leffler 	return 0;
50139beb93cSSam Leffler }
50239beb93cSSam Leffler 
50339beb93cSSam Leffler 
50439beb93cSSam Leffler static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
50539beb93cSSam Leffler {
50639beb93cSSam Leffler 	if (uuid_r == NULL) {
50739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
50839beb93cSSam Leffler 		return -1;
50939beb93cSSam Leffler 	}
51039beb93cSSam Leffler 
51139beb93cSSam Leffler 	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
51239beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
51339beb93cSSam Leffler 
51439beb93cSSam Leffler 	return 0;
51539beb93cSSam Leffler }
51639beb93cSSam Leffler 
51739beb93cSSam Leffler 
51839beb93cSSam Leffler static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
51939beb93cSSam Leffler 			      size_t pk_len)
52039beb93cSSam Leffler {
52139beb93cSSam Leffler 	if (pk == NULL || pk_len == 0) {
52239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
52339beb93cSSam Leffler 		return -1;
52439beb93cSSam Leffler 	}
52539beb93cSSam Leffler 
52639beb93cSSam Leffler 	wpabuf_free(wps->dh_pubkey_r);
52739beb93cSSam Leffler 	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
52839beb93cSSam Leffler 	if (wps->dh_pubkey_r == NULL)
52939beb93cSSam Leffler 		return -1;
53039beb93cSSam Leffler 
53139beb93cSSam Leffler 	if (wps_derive_keys(wps) < 0)
53239beb93cSSam Leffler 		return -1;
53339beb93cSSam Leffler 
53439beb93cSSam Leffler 	return 0;
53539beb93cSSam Leffler }
53639beb93cSSam Leffler 
53739beb93cSSam Leffler 
53839beb93cSSam Leffler static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
53939beb93cSSam Leffler {
54039beb93cSSam Leffler 	if (r_hash1 == NULL) {
54139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
54239beb93cSSam Leffler 		return -1;
54339beb93cSSam Leffler 	}
54439beb93cSSam Leffler 
54539beb93cSSam Leffler 	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
54639beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
54739beb93cSSam Leffler 
54839beb93cSSam Leffler 	return 0;
54939beb93cSSam Leffler }
55039beb93cSSam Leffler 
55139beb93cSSam Leffler 
55239beb93cSSam Leffler static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
55339beb93cSSam Leffler {
55439beb93cSSam Leffler 	if (r_hash2 == NULL) {
55539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
55639beb93cSSam Leffler 		return -1;
55739beb93cSSam Leffler 	}
55839beb93cSSam Leffler 
55939beb93cSSam Leffler 	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
56039beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
56139beb93cSSam Leffler 
56239beb93cSSam Leffler 	return 0;
56339beb93cSSam Leffler }
56439beb93cSSam Leffler 
56539beb93cSSam Leffler 
56639beb93cSSam Leffler static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
56739beb93cSSam Leffler {
56839beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
56939beb93cSSam Leffler 	const u8 *addr[4];
57039beb93cSSam Leffler 	size_t len[4];
57139beb93cSSam Leffler 
57239beb93cSSam Leffler 	if (r_snonce1 == NULL) {
57339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
57439beb93cSSam Leffler 		return -1;
57539beb93cSSam Leffler 	}
57639beb93cSSam Leffler 
57739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
57839beb93cSSam Leffler 			WPS_SECRET_NONCE_LEN);
57939beb93cSSam Leffler 
58039beb93cSSam Leffler 	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
58139beb93cSSam Leffler 	addr[0] = r_snonce1;
58239beb93cSSam Leffler 	len[0] = WPS_SECRET_NONCE_LEN;
58339beb93cSSam Leffler 	addr[1] = wps->psk1;
58439beb93cSSam Leffler 	len[1] = WPS_PSK_LEN;
58539beb93cSSam Leffler 	addr[2] = wpabuf_head(wps->dh_pubkey_e);
58639beb93cSSam Leffler 	len[2] = wpabuf_len(wps->dh_pubkey_e);
58739beb93cSSam Leffler 	addr[3] = wpabuf_head(wps->dh_pubkey_r);
58839beb93cSSam Leffler 	len[3] = wpabuf_len(wps->dh_pubkey_r);
58939beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
59039beb93cSSam Leffler 
59139beb93cSSam Leffler 	if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
59239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
59339beb93cSSam Leffler 			   "not match with the pre-committed value");
59439beb93cSSam Leffler 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
59539beb93cSSam Leffler 		wps_pwd_auth_fail_event(wps->wps, 1, 1);
59639beb93cSSam Leffler 		return -1;
59739beb93cSSam Leffler 	}
59839beb93cSSam Leffler 
59939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
60039beb93cSSam Leffler 		   "half of the device password");
60139beb93cSSam Leffler 
60239beb93cSSam Leffler 	return 0;
60339beb93cSSam Leffler }
60439beb93cSSam Leffler 
60539beb93cSSam Leffler 
60639beb93cSSam Leffler static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
60739beb93cSSam Leffler {
60839beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
60939beb93cSSam Leffler 	const u8 *addr[4];
61039beb93cSSam Leffler 	size_t len[4];
61139beb93cSSam Leffler 
61239beb93cSSam Leffler 	if (r_snonce2 == NULL) {
61339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
61439beb93cSSam Leffler 		return -1;
61539beb93cSSam Leffler 	}
61639beb93cSSam Leffler 
61739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
61839beb93cSSam Leffler 			WPS_SECRET_NONCE_LEN);
61939beb93cSSam Leffler 
62039beb93cSSam Leffler 	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
62139beb93cSSam Leffler 	addr[0] = r_snonce2;
62239beb93cSSam Leffler 	len[0] = WPS_SECRET_NONCE_LEN;
62339beb93cSSam Leffler 	addr[1] = wps->psk2;
62439beb93cSSam Leffler 	len[1] = WPS_PSK_LEN;
62539beb93cSSam Leffler 	addr[2] = wpabuf_head(wps->dh_pubkey_e);
62639beb93cSSam Leffler 	len[2] = wpabuf_len(wps->dh_pubkey_e);
62739beb93cSSam Leffler 	addr[3] = wpabuf_head(wps->dh_pubkey_r);
62839beb93cSSam Leffler 	len[3] = wpabuf_len(wps->dh_pubkey_r);
62939beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
63039beb93cSSam Leffler 
63139beb93cSSam Leffler 	if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
63239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
63339beb93cSSam Leffler 			   "not match with the pre-committed value");
63439beb93cSSam Leffler 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
63539beb93cSSam Leffler 		wps_pwd_auth_fail_event(wps->wps, 1, 2);
63639beb93cSSam Leffler 		return -1;
63739beb93cSSam Leffler 	}
63839beb93cSSam Leffler 
63939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
64039beb93cSSam Leffler 		   "half of the device password");
64139beb93cSSam Leffler 
64239beb93cSSam Leffler 	return 0;
64339beb93cSSam Leffler }
64439beb93cSSam Leffler 
64539beb93cSSam Leffler 
64639beb93cSSam Leffler static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
647*f05cddf9SRui Paulo 			      size_t cred_len, int wps2)
64839beb93cSSam Leffler {
64939beb93cSSam Leffler 	struct wps_parse_attr attr;
65039beb93cSSam Leffler 	struct wpabuf msg;
651*f05cddf9SRui Paulo 	int ret = 0;
65239beb93cSSam Leffler 
65339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
65439beb93cSSam Leffler 	os_memset(&wps->cred, 0, sizeof(wps->cred));
65539beb93cSSam Leffler 	wpabuf_set(&msg, cred, cred_len);
65639beb93cSSam Leffler 	if (wps_parse_msg(&msg, &attr) < 0 ||
65739beb93cSSam Leffler 	    wps_process_cred(&attr, &wps->cred))
65839beb93cSSam Leffler 		return -1;
65939beb93cSSam Leffler 
6603157ba21SRui Paulo 	if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
6613157ba21SRui Paulo 	    0) {
6623157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
6633157ba21SRui Paulo 			   MACSTR ") does not match with own address (" MACSTR
6643157ba21SRui Paulo 			   ")", MAC2STR(wps->cred.mac_addr),
6653157ba21SRui Paulo 			   MAC2STR(wps->wps->dev.mac_addr));
6663157ba21SRui Paulo 		/*
6673157ba21SRui Paulo 		 * In theory, this could be consider fatal error, but there are
6683157ba21SRui Paulo 		 * number of deployed implementations using other address here
6693157ba21SRui Paulo 		 * due to unclarity in the specification. For interoperability
6703157ba21SRui Paulo 		 * reasons, allow this to be processed since we do not really
6713157ba21SRui Paulo 		 * use the MAC Address information for anything.
6723157ba21SRui Paulo 		 */
673*f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
674*f05cddf9SRui Paulo 		if (wps2) {
675*f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
676*f05cddf9SRui Paulo 				   "MAC Address in AP Settings");
677*f05cddf9SRui Paulo 			return -1;
6783157ba21SRui Paulo 		}
679*f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
680*f05cddf9SRui Paulo 	}
681*f05cddf9SRui Paulo 
682*f05cddf9SRui Paulo #ifdef CONFIG_WPS2
683*f05cddf9SRui Paulo 	if (!(wps->cred.encr_type &
684*f05cddf9SRui Paulo 	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
685*f05cddf9SRui Paulo 		if (wps->cred.encr_type & WPS_ENCR_WEP) {
686*f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Reject Credential "
687*f05cddf9SRui Paulo 				   "due to WEP configuration");
688*f05cddf9SRui Paulo 			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
689*f05cddf9SRui Paulo 			return -2;
690*f05cddf9SRui Paulo 		}
691*f05cddf9SRui Paulo 
692*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
693*f05cddf9SRui Paulo 			   "invalid encr_type 0x%x", wps->cred.encr_type);
694*f05cddf9SRui Paulo 		return -1;
695*f05cddf9SRui Paulo 	}
696*f05cddf9SRui Paulo #endif /* CONFIG_WPS2 */
6973157ba21SRui Paulo 
69839beb93cSSam Leffler 	if (wps->wps->cred_cb) {
69939beb93cSSam Leffler 		wps->cred.cred_attr = cred - 4;
70039beb93cSSam Leffler 		wps->cred.cred_attr_len = cred_len + 4;
701*f05cddf9SRui Paulo 		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
70239beb93cSSam Leffler 		wps->cred.cred_attr = NULL;
70339beb93cSSam Leffler 		wps->cred.cred_attr_len = 0;
70439beb93cSSam Leffler 	}
70539beb93cSSam Leffler 
706*f05cddf9SRui Paulo 	return ret;
70739beb93cSSam Leffler }
70839beb93cSSam Leffler 
70939beb93cSSam Leffler 
71039beb93cSSam Leffler static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
711*f05cddf9SRui Paulo 			     size_t cred_len[], size_t num_cred, int wps2)
71239beb93cSSam Leffler {
71339beb93cSSam Leffler 	size_t i;
714*f05cddf9SRui Paulo 	int ok = 0;
71539beb93cSSam Leffler 
71639beb93cSSam Leffler 	if (wps->wps->ap)
71739beb93cSSam Leffler 		return 0;
71839beb93cSSam Leffler 
71939beb93cSSam Leffler 	if (num_cred == 0) {
72039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
72139beb93cSSam Leffler 			   "received");
72239beb93cSSam Leffler 		return -1;
72339beb93cSSam Leffler 	}
72439beb93cSSam Leffler 
72539beb93cSSam Leffler 	for (i = 0; i < num_cred; i++) {
726*f05cddf9SRui Paulo 		int res;
727*f05cddf9SRui Paulo 		res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
728*f05cddf9SRui Paulo 		if (res == 0)
729*f05cddf9SRui Paulo 			ok++;
730*f05cddf9SRui Paulo 		else if (res == -2)
731*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
732*f05cddf9SRui Paulo 		else
733*f05cddf9SRui Paulo 			return -1;
734*f05cddf9SRui Paulo 	}
735*f05cddf9SRui Paulo 
736*f05cddf9SRui Paulo 	if (ok == 0) {
737*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
738*f05cddf9SRui Paulo 			   "received");
73939beb93cSSam Leffler 		return -1;
74039beb93cSSam Leffler 	}
74139beb93cSSam Leffler 
74239beb93cSSam Leffler 	return 0;
74339beb93cSSam Leffler }
74439beb93cSSam Leffler 
74539beb93cSSam Leffler 
74639beb93cSSam Leffler static int wps_process_ap_settings_e(struct wps_data *wps,
74739beb93cSSam Leffler 				     struct wps_parse_attr *attr,
748*f05cddf9SRui Paulo 				     struct wpabuf *attrs, int wps2)
74939beb93cSSam Leffler {
75039beb93cSSam Leffler 	struct wps_credential cred;
75139beb93cSSam Leffler 
75239beb93cSSam Leffler 	if (!wps->wps->ap)
75339beb93cSSam Leffler 		return 0;
75439beb93cSSam Leffler 
75539beb93cSSam Leffler 	if (wps_process_ap_settings(attr, &cred) < 0)
75639beb93cSSam Leffler 		return -1;
75739beb93cSSam Leffler 
75839beb93cSSam Leffler 	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
75939beb93cSSam Leffler 		   "Registrar");
76039beb93cSSam Leffler 
7613157ba21SRui Paulo 	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
7623157ba21SRui Paulo 	    0) {
7633157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
7643157ba21SRui Paulo 			   MACSTR ") does not match with own address (" MACSTR
7653157ba21SRui Paulo 			   ")", MAC2STR(cred.mac_addr),
7663157ba21SRui Paulo 			   MAC2STR(wps->wps->dev.mac_addr));
7673157ba21SRui Paulo 		/*
7683157ba21SRui Paulo 		 * In theory, this could be consider fatal error, but there are
7693157ba21SRui Paulo 		 * number of deployed implementations using other address here
7703157ba21SRui Paulo 		 * due to unclarity in the specification. For interoperability
7713157ba21SRui Paulo 		 * reasons, allow this to be processed since we do not really
7723157ba21SRui Paulo 		 * use the MAC Address information for anything.
7733157ba21SRui Paulo 		 */
774*f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
775*f05cddf9SRui Paulo 		if (wps2) {
776*f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
777*f05cddf9SRui Paulo 				   "MAC Address in AP Settings");
778*f05cddf9SRui Paulo 			return -1;
7793157ba21SRui Paulo 		}
780*f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
781*f05cddf9SRui Paulo 	}
782*f05cddf9SRui Paulo 
783*f05cddf9SRui Paulo #ifdef CONFIG_WPS2
784*f05cddf9SRui Paulo 	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
785*f05cddf9SRui Paulo 	{
786*f05cddf9SRui Paulo 		if (cred.encr_type & WPS_ENCR_WEP) {
787*f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
788*f05cddf9SRui Paulo 				   "due to WEP configuration");
789*f05cddf9SRui Paulo 			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
790*f05cddf9SRui Paulo 			return -1;
791*f05cddf9SRui Paulo 		}
792*f05cddf9SRui Paulo 
793*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
794*f05cddf9SRui Paulo 			   "invalid encr_type 0x%x", cred.encr_type);
795*f05cddf9SRui Paulo 		return -1;
796*f05cddf9SRui Paulo 	}
797*f05cddf9SRui Paulo #endif /* CONFIG_WPS2 */
798*f05cddf9SRui Paulo 
799*f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
800*f05cddf9SRui Paulo 	if (wps2) {
801*f05cddf9SRui Paulo 		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
802*f05cddf9SRui Paulo 		    WPS_ENCR_TKIP ||
803*f05cddf9SRui Paulo 		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
804*f05cddf9SRui Paulo 		    WPS_AUTH_WPAPSK) {
805*f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
806*f05cddf9SRui Paulo 				   "AP Settings: WPA-Personal/TKIP only");
807*f05cddf9SRui Paulo 			wps->error_indication =
808*f05cddf9SRui Paulo 				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
809*f05cddf9SRui Paulo 			return -1;
810*f05cddf9SRui Paulo 		}
811*f05cddf9SRui Paulo 	}
812*f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
813*f05cddf9SRui Paulo 
814*f05cddf9SRui Paulo #ifdef CONFIG_WPS2
815*f05cddf9SRui Paulo 	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
816*f05cddf9SRui Paulo 	{
817*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
818*f05cddf9SRui Paulo 			   "TKIP+AES");
819*f05cddf9SRui Paulo 		cred.encr_type |= WPS_ENCR_AES;
820*f05cddf9SRui Paulo 	}
821*f05cddf9SRui Paulo 
822*f05cddf9SRui Paulo 	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
823*f05cddf9SRui Paulo 	    WPS_AUTH_WPAPSK) {
824*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
825*f05cddf9SRui Paulo 			   "WPAPSK+WPA2PSK");
826*f05cddf9SRui Paulo 		cred.auth_type |= WPS_AUTH_WPA2PSK;
827*f05cddf9SRui Paulo 	}
828*f05cddf9SRui Paulo #endif /* CONFIG_WPS2 */
8293157ba21SRui Paulo 
83039beb93cSSam Leffler 	if (wps->wps->cred_cb) {
83139beb93cSSam Leffler 		cred.cred_attr = wpabuf_head(attrs);
83239beb93cSSam Leffler 		cred.cred_attr_len = wpabuf_len(attrs);
83339beb93cSSam Leffler 		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
83439beb93cSSam Leffler 	}
83539beb93cSSam Leffler 
83639beb93cSSam Leffler 	return 0;
83739beb93cSSam Leffler }
83839beb93cSSam Leffler 
83939beb93cSSam Leffler 
84039beb93cSSam Leffler static enum wps_process_res wps_process_m2(struct wps_data *wps,
84139beb93cSSam Leffler 					   const struct wpabuf *msg,
84239beb93cSSam Leffler 					   struct wps_parse_attr *attr)
84339beb93cSSam Leffler {
84439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M2");
84539beb93cSSam Leffler 
84639beb93cSSam Leffler 	if (wps->state != RECV_M2) {
84739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
84839beb93cSSam Leffler 			   "receiving M2", wps->state);
84939beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
85039beb93cSSam Leffler 		return WPS_CONTINUE;
85139beb93cSSam Leffler 	}
85239beb93cSSam Leffler 
85339beb93cSSam Leffler 	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
85439beb93cSSam Leffler 	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
855e28a4053SRui Paulo 	    wps_process_uuid_r(wps, attr->uuid_r)) {
85639beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
85739beb93cSSam Leffler 		return WPS_CONTINUE;
85839beb93cSSam Leffler 	}
85939beb93cSSam Leffler 
860*f05cddf9SRui Paulo 	/*
861*f05cddf9SRui Paulo 	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
862*f05cddf9SRui Paulo 	 * special locked mode is used to allow protocol run up to M7 in order
863*f05cddf9SRui Paulo 	 * to support external Registrars that only learn the current AP
864*f05cddf9SRui Paulo 	 * configuration without changing it.
865*f05cddf9SRui Paulo 	 */
866e28a4053SRui Paulo 	if (wps->wps->ap &&
867*f05cddf9SRui Paulo 	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
868*f05cddf9SRui Paulo 	     wps->dev_password == NULL)) {
86939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
87039beb93cSSam Leffler 			   "registration of a new Registrar");
87139beb93cSSam Leffler 		wps->config_error = WPS_CFG_SETUP_LOCKED;
87239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
87339beb93cSSam Leffler 		return WPS_CONTINUE;
87439beb93cSSam Leffler 	}
87539beb93cSSam Leffler 
876e28a4053SRui Paulo 	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
877e28a4053SRui Paulo 	    wps_process_authenticator(wps, attr->authenticator, msg) ||
878e28a4053SRui Paulo 	    wps_process_device_attrs(&wps->peer_dev, attr)) {
879e28a4053SRui Paulo 		wps->state = SEND_WSC_NACK;
880e28a4053SRui Paulo 		return WPS_CONTINUE;
881e28a4053SRui Paulo 	}
882e28a4053SRui Paulo 
88339beb93cSSam Leffler 	wps->state = SEND_M3;
88439beb93cSSam Leffler 	return WPS_CONTINUE;
88539beb93cSSam Leffler }
88639beb93cSSam Leffler 
88739beb93cSSam Leffler 
88839beb93cSSam Leffler static enum wps_process_res wps_process_m2d(struct wps_data *wps,
88939beb93cSSam Leffler 					    struct wps_parse_attr *attr)
89039beb93cSSam Leffler {
89139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
89239beb93cSSam Leffler 
89339beb93cSSam Leffler 	if (wps->state != RECV_M2) {
89439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
89539beb93cSSam Leffler 			   "receiving M2D", wps->state);
89639beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
89739beb93cSSam Leffler 		return WPS_CONTINUE;
89839beb93cSSam Leffler 	}
89939beb93cSSam Leffler 
90039beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
90139beb93cSSam Leffler 			  attr->manufacturer, attr->manufacturer_len);
90239beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
90339beb93cSSam Leffler 			  attr->model_name, attr->model_name_len);
90439beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
90539beb93cSSam Leffler 			  attr->model_number, attr->model_number_len);
90639beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
90739beb93cSSam Leffler 			  attr->serial_number, attr->serial_number_len);
90839beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
90939beb93cSSam Leffler 			  attr->dev_name, attr->dev_name_len);
91039beb93cSSam Leffler 
91139beb93cSSam Leffler 	if (wps->wps->event_cb) {
91239beb93cSSam Leffler 		union wps_event_data data;
91339beb93cSSam Leffler 		struct wps_event_m2d *m2d = &data.m2d;
91439beb93cSSam Leffler 		os_memset(&data, 0, sizeof(data));
91539beb93cSSam Leffler 		if (attr->config_methods)
91639beb93cSSam Leffler 			m2d->config_methods =
91739beb93cSSam Leffler 				WPA_GET_BE16(attr->config_methods);
91839beb93cSSam Leffler 		m2d->manufacturer = attr->manufacturer;
91939beb93cSSam Leffler 		m2d->manufacturer_len = attr->manufacturer_len;
92039beb93cSSam Leffler 		m2d->model_name = attr->model_name;
92139beb93cSSam Leffler 		m2d->model_name_len = attr->model_name_len;
92239beb93cSSam Leffler 		m2d->model_number = attr->model_number;
92339beb93cSSam Leffler 		m2d->model_number_len = attr->model_number_len;
92439beb93cSSam Leffler 		m2d->serial_number = attr->serial_number;
92539beb93cSSam Leffler 		m2d->serial_number_len = attr->serial_number_len;
92639beb93cSSam Leffler 		m2d->dev_name = attr->dev_name;
92739beb93cSSam Leffler 		m2d->dev_name_len = attr->dev_name_len;
92839beb93cSSam Leffler 		m2d->primary_dev_type = attr->primary_dev_type;
92939beb93cSSam Leffler 		if (attr->config_error)
93039beb93cSSam Leffler 			m2d->config_error =
93139beb93cSSam Leffler 				WPA_GET_BE16(attr->config_error);
93239beb93cSSam Leffler 		if (attr->dev_password_id)
93339beb93cSSam Leffler 			m2d->dev_password_id =
93439beb93cSSam Leffler 				WPA_GET_BE16(attr->dev_password_id);
93539beb93cSSam Leffler 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
93639beb93cSSam Leffler 	}
93739beb93cSSam Leffler 
93839beb93cSSam Leffler 	wps->state = RECEIVED_M2D;
93939beb93cSSam Leffler 	return WPS_CONTINUE;
94039beb93cSSam Leffler }
94139beb93cSSam Leffler 
94239beb93cSSam Leffler 
94339beb93cSSam Leffler static enum wps_process_res wps_process_m4(struct wps_data *wps,
94439beb93cSSam Leffler 					   const struct wpabuf *msg,
94539beb93cSSam Leffler 					   struct wps_parse_attr *attr)
94639beb93cSSam Leffler {
94739beb93cSSam Leffler 	struct wpabuf *decrypted;
94839beb93cSSam Leffler 	struct wps_parse_attr eattr;
94939beb93cSSam Leffler 
95039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M4");
95139beb93cSSam Leffler 
95239beb93cSSam Leffler 	if (wps->state != RECV_M4) {
95339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
95439beb93cSSam Leffler 			   "receiving M4", wps->state);
95539beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
95639beb93cSSam Leffler 		return WPS_CONTINUE;
95739beb93cSSam Leffler 	}
95839beb93cSSam Leffler 
95939beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
96039beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg) ||
96139beb93cSSam Leffler 	    wps_process_r_hash1(wps, attr->r_hash1) ||
96239beb93cSSam Leffler 	    wps_process_r_hash2(wps, attr->r_hash2)) {
96339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
96439beb93cSSam Leffler 		return WPS_CONTINUE;
96539beb93cSSam Leffler 	}
96639beb93cSSam Leffler 
96739beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
96839beb93cSSam Leffler 					      attr->encr_settings_len);
96939beb93cSSam Leffler 	if (decrypted == NULL) {
97039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
97139beb93cSSam Leffler 			   "Settings attribute");
97239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
97339beb93cSSam Leffler 		return WPS_CONTINUE;
97439beb93cSSam Leffler 	}
97539beb93cSSam Leffler 
976*f05cddf9SRui Paulo 	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
977*f05cddf9SRui Paulo 		wpabuf_free(decrypted);
978*f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
979*f05cddf9SRui Paulo 		return WPS_CONTINUE;
980*f05cddf9SRui Paulo 	}
981*f05cddf9SRui Paulo 
98239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
98339beb93cSSam Leffler 		   "attribute");
98439beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
98539beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
98639beb93cSSam Leffler 	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
98739beb93cSSam Leffler 		wpabuf_free(decrypted);
98839beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
98939beb93cSSam Leffler 		return WPS_CONTINUE;
99039beb93cSSam Leffler 	}
99139beb93cSSam Leffler 	wpabuf_free(decrypted);
99239beb93cSSam Leffler 
99339beb93cSSam Leffler 	wps->state = SEND_M5;
99439beb93cSSam Leffler 	return WPS_CONTINUE;
99539beb93cSSam Leffler }
99639beb93cSSam Leffler 
99739beb93cSSam Leffler 
99839beb93cSSam Leffler static enum wps_process_res wps_process_m6(struct wps_data *wps,
99939beb93cSSam Leffler 					   const struct wpabuf *msg,
100039beb93cSSam Leffler 					   struct wps_parse_attr *attr)
100139beb93cSSam Leffler {
100239beb93cSSam Leffler 	struct wpabuf *decrypted;
100339beb93cSSam Leffler 	struct wps_parse_attr eattr;
100439beb93cSSam Leffler 
100539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M6");
100639beb93cSSam Leffler 
100739beb93cSSam Leffler 	if (wps->state != RECV_M6) {
100839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
100939beb93cSSam Leffler 			   "receiving M6", wps->state);
101039beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
101139beb93cSSam Leffler 		return WPS_CONTINUE;
101239beb93cSSam Leffler 	}
101339beb93cSSam Leffler 
101439beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
101539beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg)) {
101639beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
101739beb93cSSam Leffler 		return WPS_CONTINUE;
101839beb93cSSam Leffler 	}
101939beb93cSSam Leffler 
102039beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
102139beb93cSSam Leffler 					      attr->encr_settings_len);
102239beb93cSSam Leffler 	if (decrypted == NULL) {
102339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
102439beb93cSSam Leffler 			   "Settings attribute");
102539beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
102639beb93cSSam Leffler 		return WPS_CONTINUE;
102739beb93cSSam Leffler 	}
102839beb93cSSam Leffler 
1029*f05cddf9SRui Paulo 	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1030*f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1031*f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1032*f05cddf9SRui Paulo 		return WPS_CONTINUE;
1033*f05cddf9SRui Paulo 	}
1034*f05cddf9SRui Paulo 
103539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
103639beb93cSSam Leffler 		   "attribute");
103739beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
103839beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
103939beb93cSSam Leffler 	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
104039beb93cSSam Leffler 		wpabuf_free(decrypted);
104139beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
104239beb93cSSam Leffler 		return WPS_CONTINUE;
104339beb93cSSam Leffler 	}
104439beb93cSSam Leffler 	wpabuf_free(decrypted);
104539beb93cSSam Leffler 
1046*f05cddf9SRui Paulo 	if (wps->wps->ap)
1047*f05cddf9SRui Paulo 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1048*f05cddf9SRui Paulo 				   NULL);
1049*f05cddf9SRui Paulo 
105039beb93cSSam Leffler 	wps->state = SEND_M7;
105139beb93cSSam Leffler 	return WPS_CONTINUE;
105239beb93cSSam Leffler }
105339beb93cSSam Leffler 
105439beb93cSSam Leffler 
105539beb93cSSam Leffler static enum wps_process_res wps_process_m8(struct wps_data *wps,
105639beb93cSSam Leffler 					   const struct wpabuf *msg,
105739beb93cSSam Leffler 					   struct wps_parse_attr *attr)
105839beb93cSSam Leffler {
105939beb93cSSam Leffler 	struct wpabuf *decrypted;
106039beb93cSSam Leffler 	struct wps_parse_attr eattr;
106139beb93cSSam Leffler 
106239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M8");
106339beb93cSSam Leffler 
106439beb93cSSam Leffler 	if (wps->state != RECV_M8) {
106539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
106639beb93cSSam Leffler 			   "receiving M8", wps->state);
106739beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
106839beb93cSSam Leffler 		return WPS_CONTINUE;
106939beb93cSSam Leffler 	}
107039beb93cSSam Leffler 
107139beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
107239beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg)) {
107339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
107439beb93cSSam Leffler 		return WPS_CONTINUE;
107539beb93cSSam Leffler 	}
107639beb93cSSam Leffler 
1077*f05cddf9SRui Paulo 	if (wps->wps->ap && wps->wps->ap_setup_locked) {
1078*f05cddf9SRui Paulo 		/*
1079*f05cddf9SRui Paulo 		 * Stop here if special ap_setup_locked == 2 mode allowed the
1080*f05cddf9SRui Paulo 		 * protocol to continue beyond M2. This allows ER to learn the
1081*f05cddf9SRui Paulo 		 * current AP settings without changing them.
1082*f05cddf9SRui Paulo 		 */
1083*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1084*f05cddf9SRui Paulo 			   "registration of a new Registrar");
1085*f05cddf9SRui Paulo 		wps->config_error = WPS_CFG_SETUP_LOCKED;
1086*f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1087*f05cddf9SRui Paulo 		return WPS_CONTINUE;
1088*f05cddf9SRui Paulo 	}
1089*f05cddf9SRui Paulo 
109039beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
109139beb93cSSam Leffler 					      attr->encr_settings_len);
109239beb93cSSam Leffler 	if (decrypted == NULL) {
109339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
109439beb93cSSam Leffler 			   "Settings attribute");
109539beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
109639beb93cSSam Leffler 		return WPS_CONTINUE;
109739beb93cSSam Leffler 	}
109839beb93cSSam Leffler 
1099*f05cddf9SRui Paulo 	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1100*f05cddf9SRui Paulo 				 attr->version2 != NULL) < 0) {
1101*f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1102*f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1103*f05cddf9SRui Paulo 		return WPS_CONTINUE;
1104*f05cddf9SRui Paulo 	}
1105*f05cddf9SRui Paulo 
110639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
110739beb93cSSam Leffler 		   "attribute");
110839beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
110939beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
111039beb93cSSam Leffler 	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1111*f05cddf9SRui Paulo 			      eattr.num_cred, attr->version2 != NULL) ||
1112*f05cddf9SRui Paulo 	    wps_process_ap_settings_e(wps, &eattr, decrypted,
1113*f05cddf9SRui Paulo 				      attr->version2 != NULL)) {
111439beb93cSSam Leffler 		wpabuf_free(decrypted);
111539beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
111639beb93cSSam Leffler 		return WPS_CONTINUE;
111739beb93cSSam Leffler 	}
111839beb93cSSam Leffler 	wpabuf_free(decrypted);
111939beb93cSSam Leffler 
112039beb93cSSam Leffler 	wps->state = WPS_MSG_DONE;
112139beb93cSSam Leffler 	return WPS_CONTINUE;
112239beb93cSSam Leffler }
112339beb93cSSam Leffler 
112439beb93cSSam Leffler 
112539beb93cSSam Leffler static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
112639beb93cSSam Leffler 						const struct wpabuf *msg)
112739beb93cSSam Leffler {
112839beb93cSSam Leffler 	struct wps_parse_attr attr;
112939beb93cSSam Leffler 	enum wps_process_res ret = WPS_CONTINUE;
113039beb93cSSam Leffler 
113139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
113239beb93cSSam Leffler 
113339beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
113439beb93cSSam Leffler 		return WPS_FAILURE;
113539beb93cSSam Leffler 
113639beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1137*f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
113839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
113939beb93cSSam Leffler 		return WPS_FAILURE;
114039beb93cSSam Leffler 	}
114139beb93cSSam Leffler 
114239beb93cSSam Leffler 	if (attr.msg_type == NULL) {
114339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1144*f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1145*f05cddf9SRui Paulo 		return WPS_CONTINUE;
114639beb93cSSam Leffler 	}
114739beb93cSSam Leffler 
114839beb93cSSam Leffler 	switch (*attr.msg_type) {
114939beb93cSSam Leffler 	case WPS_M2:
1150*f05cddf9SRui Paulo 		if (wps_validate_m2(msg) < 0)
1151*f05cddf9SRui Paulo 			return WPS_FAILURE;
115239beb93cSSam Leffler 		ret = wps_process_m2(wps, msg, &attr);
115339beb93cSSam Leffler 		break;
115439beb93cSSam Leffler 	case WPS_M2D:
1155*f05cddf9SRui Paulo 		if (wps_validate_m2d(msg) < 0)
1156*f05cddf9SRui Paulo 			return WPS_FAILURE;
115739beb93cSSam Leffler 		ret = wps_process_m2d(wps, &attr);
115839beb93cSSam Leffler 		break;
115939beb93cSSam Leffler 	case WPS_M4:
1160*f05cddf9SRui Paulo 		if (wps_validate_m4(msg) < 0)
1161*f05cddf9SRui Paulo 			return WPS_FAILURE;
116239beb93cSSam Leffler 		ret = wps_process_m4(wps, msg, &attr);
116339beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1164*f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
1165*f05cddf9SRui Paulo 				       wps->error_indication);
116639beb93cSSam Leffler 		break;
116739beb93cSSam Leffler 	case WPS_M6:
1168*f05cddf9SRui Paulo 		if (wps_validate_m6(msg) < 0)
1169*f05cddf9SRui Paulo 			return WPS_FAILURE;
117039beb93cSSam Leffler 		ret = wps_process_m6(wps, msg, &attr);
117139beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1172*f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
1173*f05cddf9SRui Paulo 				       wps->error_indication);
117439beb93cSSam Leffler 		break;
117539beb93cSSam Leffler 	case WPS_M8:
1176*f05cddf9SRui Paulo 		if (wps_validate_m8(msg) < 0)
1177*f05cddf9SRui Paulo 			return WPS_FAILURE;
117839beb93cSSam Leffler 		ret = wps_process_m8(wps, msg, &attr);
117939beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1180*f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
1181*f05cddf9SRui Paulo 				       wps->error_indication);
118239beb93cSSam Leffler 		break;
118339beb93cSSam Leffler 	default:
118439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
118539beb93cSSam Leffler 			   *attr.msg_type);
118639beb93cSSam Leffler 		return WPS_FAILURE;
118739beb93cSSam Leffler 	}
118839beb93cSSam Leffler 
118939beb93cSSam Leffler 	/*
119039beb93cSSam Leffler 	 * Save a copy of the last message for Authenticator derivation if we
119139beb93cSSam Leffler 	 * are continuing. However, skip M2D since it is not authenticated and
119239beb93cSSam Leffler 	 * neither is the ACK/NACK response frame. This allows the possibly
119339beb93cSSam Leffler 	 * following M2 to be processed correctly by using the previously sent
119439beb93cSSam Leffler 	 * M1 in Authenticator derivation.
119539beb93cSSam Leffler 	 */
119639beb93cSSam Leffler 	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
119739beb93cSSam Leffler 		/* Save a copy of the last message for Authenticator derivation
119839beb93cSSam Leffler 		 */
119939beb93cSSam Leffler 		wpabuf_free(wps->last_msg);
120039beb93cSSam Leffler 		wps->last_msg = wpabuf_dup(msg);
120139beb93cSSam Leffler 	}
120239beb93cSSam Leffler 
120339beb93cSSam Leffler 	return ret;
120439beb93cSSam Leffler }
120539beb93cSSam Leffler 
120639beb93cSSam Leffler 
120739beb93cSSam Leffler static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
120839beb93cSSam Leffler 						const struct wpabuf *msg)
120939beb93cSSam Leffler {
121039beb93cSSam Leffler 	struct wps_parse_attr attr;
121139beb93cSSam Leffler 
121239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
121339beb93cSSam Leffler 
121439beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
121539beb93cSSam Leffler 		return WPS_FAILURE;
121639beb93cSSam Leffler 
121739beb93cSSam Leffler 	if (attr.msg_type == NULL) {
121839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
121939beb93cSSam Leffler 		return WPS_FAILURE;
122039beb93cSSam Leffler 	}
122139beb93cSSam Leffler 
122239beb93cSSam Leffler 	if (*attr.msg_type != WPS_WSC_ACK) {
122339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
122439beb93cSSam Leffler 			   *attr.msg_type);
122539beb93cSSam Leffler 		return WPS_FAILURE;
122639beb93cSSam Leffler 	}
122739beb93cSSam Leffler 
122839beb93cSSam Leffler 	if (attr.registrar_nonce == NULL ||
1229*f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
123039beb93cSSam Leffler 	{
123139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
123239beb93cSSam Leffler 		return WPS_FAILURE;
123339beb93cSSam Leffler 	}
123439beb93cSSam Leffler 
123539beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1236*f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
123739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
123839beb93cSSam Leffler 		return WPS_FAILURE;
123939beb93cSSam Leffler 	}
124039beb93cSSam Leffler 
124139beb93cSSam Leffler 	if (wps->state == RECV_ACK && wps->wps->ap) {
124239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
124339beb93cSSam Leffler 			   "completed successfully");
124439beb93cSSam Leffler 		wps_success_event(wps->wps);
124539beb93cSSam Leffler 		wps->state = WPS_FINISHED;
124639beb93cSSam Leffler 		return WPS_DONE;
124739beb93cSSam Leffler 	}
124839beb93cSSam Leffler 
124939beb93cSSam Leffler 	return WPS_FAILURE;
125039beb93cSSam Leffler }
125139beb93cSSam Leffler 
125239beb93cSSam Leffler 
125339beb93cSSam Leffler static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
125439beb93cSSam Leffler 						 const struct wpabuf *msg)
125539beb93cSSam Leffler {
125639beb93cSSam Leffler 	struct wps_parse_attr attr;
1257*f05cddf9SRui Paulo 	u16 config_error;
125839beb93cSSam Leffler 
125939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
126039beb93cSSam Leffler 
126139beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
126239beb93cSSam Leffler 		return WPS_FAILURE;
126339beb93cSSam Leffler 
126439beb93cSSam Leffler 	if (attr.msg_type == NULL) {
126539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
126639beb93cSSam Leffler 		return WPS_FAILURE;
126739beb93cSSam Leffler 	}
126839beb93cSSam Leffler 
126939beb93cSSam Leffler 	if (*attr.msg_type != WPS_WSC_NACK) {
127039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
127139beb93cSSam Leffler 			   *attr.msg_type);
127239beb93cSSam Leffler 		return WPS_FAILURE;
127339beb93cSSam Leffler 	}
127439beb93cSSam Leffler 
127539beb93cSSam Leffler 	if (attr.registrar_nonce == NULL ||
1276*f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
127739beb93cSSam Leffler 	{
127839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
127939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
128039beb93cSSam Leffler 			    attr.registrar_nonce, WPS_NONCE_LEN);
128139beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
128239beb93cSSam Leffler 			    wps->nonce_r, WPS_NONCE_LEN);
128339beb93cSSam Leffler 		return WPS_FAILURE;
128439beb93cSSam Leffler 	}
128539beb93cSSam Leffler 
128639beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1287*f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
128839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
128939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
129039beb93cSSam Leffler 			    attr.enrollee_nonce, WPS_NONCE_LEN);
129139beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
129239beb93cSSam Leffler 			    wps->nonce_e, WPS_NONCE_LEN);
129339beb93cSSam Leffler 		return WPS_FAILURE;
129439beb93cSSam Leffler 	}
129539beb93cSSam Leffler 
129639beb93cSSam Leffler 	if (attr.config_error == NULL) {
129739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
129839beb93cSSam Leffler 			   "in WSC_NACK");
129939beb93cSSam Leffler 		return WPS_FAILURE;
130039beb93cSSam Leffler 	}
130139beb93cSSam Leffler 
1302*f05cddf9SRui Paulo 	config_error = WPA_GET_BE16(attr.config_error);
130339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1304*f05cddf9SRui Paulo 		   "Configuration Error %d", config_error);
130539beb93cSSam Leffler 
130639beb93cSSam Leffler 	switch (wps->state) {
130739beb93cSSam Leffler 	case RECV_M4:
1308*f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M3, config_error,
1309*f05cddf9SRui Paulo 			       wps->error_indication);
131039beb93cSSam Leffler 		break;
131139beb93cSSam Leffler 	case RECV_M6:
1312*f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M5, config_error,
1313*f05cddf9SRui Paulo 			       wps->error_indication);
131439beb93cSSam Leffler 		break;
131539beb93cSSam Leffler 	case RECV_M8:
1316*f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M7, config_error,
1317*f05cddf9SRui Paulo 			       wps->error_indication);
131839beb93cSSam Leffler 		break;
131939beb93cSSam Leffler 	default:
132039beb93cSSam Leffler 		break;
132139beb93cSSam Leffler 	}
132239beb93cSSam Leffler 
132339beb93cSSam Leffler 	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
132439beb93cSSam Leffler 	 * Enrollee is Authenticator */
132539beb93cSSam Leffler 	wps->state = SEND_WSC_NACK;
132639beb93cSSam Leffler 
132739beb93cSSam Leffler 	return WPS_FAILURE;
132839beb93cSSam Leffler }
132939beb93cSSam Leffler 
133039beb93cSSam Leffler 
133139beb93cSSam Leffler enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
133239beb93cSSam Leffler 					      enum wsc_op_code op_code,
133339beb93cSSam Leffler 					      const struct wpabuf *msg)
133439beb93cSSam Leffler {
133539beb93cSSam Leffler 
133639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
133739beb93cSSam Leffler 		   "op_code=%d)",
133839beb93cSSam Leffler 		   (unsigned long) wpabuf_len(msg), op_code);
133939beb93cSSam Leffler 
13403157ba21SRui Paulo 	if (op_code == WSC_UPnP) {
13413157ba21SRui Paulo 		/* Determine the OpCode based on message type attribute */
13423157ba21SRui Paulo 		struct wps_parse_attr attr;
13433157ba21SRui Paulo 		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
13443157ba21SRui Paulo 			if (*attr.msg_type == WPS_WSC_ACK)
13453157ba21SRui Paulo 				op_code = WSC_ACK;
13463157ba21SRui Paulo 			else if (*attr.msg_type == WPS_WSC_NACK)
13473157ba21SRui Paulo 				op_code = WSC_NACK;
13483157ba21SRui Paulo 		}
13493157ba21SRui Paulo 	}
13503157ba21SRui Paulo 
135139beb93cSSam Leffler 	switch (op_code) {
135239beb93cSSam Leffler 	case WSC_MSG:
135339beb93cSSam Leffler 	case WSC_UPnP:
135439beb93cSSam Leffler 		return wps_process_wsc_msg(wps, msg);
135539beb93cSSam Leffler 	case WSC_ACK:
1356*f05cddf9SRui Paulo 		if (wps_validate_wsc_ack(msg) < 0)
1357*f05cddf9SRui Paulo 			return WPS_FAILURE;
135839beb93cSSam Leffler 		return wps_process_wsc_ack(wps, msg);
135939beb93cSSam Leffler 	case WSC_NACK:
1360*f05cddf9SRui Paulo 		if (wps_validate_wsc_nack(msg) < 0)
1361*f05cddf9SRui Paulo 			return WPS_FAILURE;
136239beb93cSSam Leffler 		return wps_process_wsc_nack(wps, msg);
136339beb93cSSam Leffler 	default:
136439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
136539beb93cSSam Leffler 		return WPS_FAILURE;
136639beb93cSSam Leffler 	}
136739beb93cSSam Leffler }
1368