xref: /freebsd/contrib/wpa/src/wps/wps_enrollee.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * Wi-Fi Protected Setup - Enrollee
339beb93cSSam Leffler  * Copyright (c) 2008, 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/crypto.h"
13e28a4053SRui Paulo #include "crypto/sha256.h"
14f05cddf9SRui 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_wps_state(struct wps_data *wps, struct wpabuf *msg)
2039beb93cSSam Leffler {
2139beb93cSSam Leffler 	u8 state;
2239beb93cSSam Leffler 	if (wps->wps->ap)
2339beb93cSSam Leffler 		state = wps->wps->wps_state;
2439beb93cSSam Leffler 	else
2539beb93cSSam Leffler 		state = WPS_STATE_NOT_CONFIGURED;
2639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
2739beb93cSSam Leffler 		   state);
2839beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_WPS_STATE);
2939beb93cSSam Leffler 	wpabuf_put_be16(msg, 1);
303157ba21SRui Paulo 	wpabuf_put_u8(msg, state);
3139beb93cSSam Leffler 	return 0;
3239beb93cSSam Leffler }
3339beb93cSSam Leffler 
3439beb93cSSam Leffler 
3539beb93cSSam Leffler static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
3639beb93cSSam Leffler {
3739beb93cSSam Leffler 	u8 *hash;
3839beb93cSSam Leffler 	const u8 *addr[4];
3939beb93cSSam Leffler 	size_t len[4];
4039beb93cSSam Leffler 
41f05cddf9SRui Paulo 	if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
4239beb93cSSam Leffler 		return -1;
4339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
4439beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
4539beb93cSSam Leffler 		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
4639beb93cSSam Leffler 
4739beb93cSSam Leffler 	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
4839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
4939beb93cSSam Leffler 			   "E-Hash derivation");
5039beb93cSSam Leffler 		return -1;
5139beb93cSSam Leffler 	}
5239beb93cSSam Leffler 
5339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
5439beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_HASH1);
5539beb93cSSam Leffler 	wpabuf_put_be16(msg, SHA256_MAC_LEN);
5639beb93cSSam Leffler 	hash = wpabuf_put(msg, SHA256_MAC_LEN);
5739beb93cSSam Leffler 	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
5839beb93cSSam Leffler 	addr[0] = wps->snonce;
5939beb93cSSam Leffler 	len[0] = WPS_SECRET_NONCE_LEN;
6039beb93cSSam Leffler 	addr[1] = wps->psk1;
6139beb93cSSam Leffler 	len[1] = WPS_PSK_LEN;
6239beb93cSSam Leffler 	addr[2] = wpabuf_head(wps->dh_pubkey_e);
6339beb93cSSam Leffler 	len[2] = wpabuf_len(wps->dh_pubkey_e);
6439beb93cSSam Leffler 	addr[3] = wpabuf_head(wps->dh_pubkey_r);
6539beb93cSSam Leffler 	len[3] = wpabuf_len(wps->dh_pubkey_r);
6639beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
6739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
6839beb93cSSam Leffler 
6939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
7039beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_HASH2);
7139beb93cSSam Leffler 	wpabuf_put_be16(msg, SHA256_MAC_LEN);
7239beb93cSSam Leffler 	hash = wpabuf_put(msg, SHA256_MAC_LEN);
7339beb93cSSam Leffler 	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
7439beb93cSSam Leffler 	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
7539beb93cSSam Leffler 	addr[1] = wps->psk2;
7639beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
7739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
7839beb93cSSam Leffler 
7939beb93cSSam Leffler 	return 0;
8039beb93cSSam Leffler }
8139beb93cSSam Leffler 
8239beb93cSSam Leffler 
8339beb93cSSam Leffler static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
8439beb93cSSam Leffler {
8539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
8639beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_SNONCE1);
8739beb93cSSam Leffler 	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
8839beb93cSSam Leffler 	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
8939beb93cSSam Leffler 	return 0;
9039beb93cSSam Leffler }
9139beb93cSSam Leffler 
9239beb93cSSam Leffler 
9339beb93cSSam Leffler static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
9439beb93cSSam Leffler {
9539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
9639beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_E_SNONCE2);
9739beb93cSSam Leffler 	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
9839beb93cSSam Leffler 	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
9939beb93cSSam Leffler 			WPS_SECRET_NONCE_LEN);
10039beb93cSSam Leffler 	return 0;
10139beb93cSSam Leffler }
10239beb93cSSam Leffler 
10339beb93cSSam Leffler 
10439beb93cSSam Leffler static struct wpabuf * wps_build_m1(struct wps_data *wps)
10539beb93cSSam Leffler {
10639beb93cSSam Leffler 	struct wpabuf *msg;
107f05cddf9SRui Paulo 	u16 config_methods;
10839beb93cSSam Leffler 
109f05cddf9SRui Paulo 	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
11039beb93cSSam Leffler 		return NULL;
11139beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
11239beb93cSSam Leffler 		    wps->nonce_e, WPS_NONCE_LEN);
11339beb93cSSam Leffler 
11439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
11539beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
11639beb93cSSam Leffler 	if (msg == NULL)
11739beb93cSSam Leffler 		return NULL;
11839beb93cSSam Leffler 
119f05cddf9SRui Paulo 	config_methods = wps->wps->config_methods;
120f05cddf9SRui Paulo 	if (wps->wps->ap && !wps->pbc_in_m1 &&
121f05cddf9SRui Paulo 	    (wps->dev_password_len != 0 ||
122f05cddf9SRui Paulo 	     (config_methods & WPS_CONFIG_DISPLAY))) {
123f05cddf9SRui Paulo 		/*
124f05cddf9SRui Paulo 		 * These are the methods that the AP supports as an Enrollee
125f05cddf9SRui Paulo 		 * for adding external Registrars, so remove PushButton.
126f05cddf9SRui Paulo 		 *
127f05cddf9SRui Paulo 		 * As a workaround for Windows 7 mechanism for probing WPS
128f05cddf9SRui Paulo 		 * capabilities from M1, leave PushButton option if no PIN
129f05cddf9SRui Paulo 		 * method is available or if WPS configuration enables PBC
130f05cddf9SRui Paulo 		 * workaround.
131f05cddf9SRui Paulo 		 */
132f05cddf9SRui Paulo 		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
133f05cddf9SRui Paulo 		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
134f05cddf9SRui Paulo 				    WPS_CONFIG_PHY_PUSHBUTTON);
135f05cddf9SRui Paulo 	}
136f05cddf9SRui Paulo 
13739beb93cSSam Leffler 	if (wps_build_version(msg) ||
13839beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M1) ||
13939beb93cSSam Leffler 	    wps_build_uuid_e(msg, wps->uuid_e) ||
140*5b9c547cSRui Paulo 	    wps_build_mac_addr(msg, wps->mac_addr_e) ||
14139beb93cSSam Leffler 	    wps_build_enrollee_nonce(wps, msg) ||
14239beb93cSSam Leffler 	    wps_build_public_key(wps, msg) ||
14339beb93cSSam Leffler 	    wps_build_auth_type_flags(wps, msg) ||
14439beb93cSSam Leffler 	    wps_build_encr_type_flags(wps, msg) ||
14539beb93cSSam Leffler 	    wps_build_conn_type_flags(wps, msg) ||
146f05cddf9SRui Paulo 	    wps_build_config_methods(msg, config_methods) ||
14739beb93cSSam Leffler 	    wps_build_wps_state(wps, msg) ||
14839beb93cSSam Leffler 	    wps_build_device_attrs(&wps->wps->dev, msg) ||
149*5b9c547cSRui Paulo 	    wps_build_rf_bands(&wps->wps->dev, msg,
150*5b9c547cSRui Paulo 			       wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
15139beb93cSSam Leffler 	    wps_build_assoc_state(wps, msg) ||
15239beb93cSSam Leffler 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
15339beb93cSSam Leffler 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
154f05cddf9SRui Paulo 	    wps_build_os_version(&wps->wps->dev, msg) ||
155f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
156f05cddf9SRui Paulo 	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
15739beb93cSSam Leffler 		wpabuf_free(msg);
15839beb93cSSam Leffler 		return NULL;
15939beb93cSSam Leffler 	}
16039beb93cSSam Leffler 
16139beb93cSSam Leffler 	wps->state = RECV_M2;
16239beb93cSSam Leffler 	return msg;
16339beb93cSSam Leffler }
16439beb93cSSam Leffler 
16539beb93cSSam Leffler 
16639beb93cSSam Leffler static struct wpabuf * wps_build_m3(struct wps_data *wps)
16739beb93cSSam Leffler {
16839beb93cSSam Leffler 	struct wpabuf *msg;
16939beb93cSSam Leffler 
17039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
17139beb93cSSam Leffler 
17239beb93cSSam Leffler 	if (wps->dev_password == NULL) {
17339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
17439beb93cSSam Leffler 		return NULL;
17539beb93cSSam Leffler 	}
17639beb93cSSam Leffler 	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
17739beb93cSSam Leffler 
178*5b9c547cSRui Paulo 	if (wps->wps->ap && random_pool_ready() != 1) {
179*5b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
180*5b9c547cSRui Paulo 			   "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
181*5b9c547cSRui Paulo 		return NULL;
182*5b9c547cSRui Paulo 	}
183*5b9c547cSRui Paulo 
18439beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
18539beb93cSSam Leffler 	if (msg == NULL)
18639beb93cSSam Leffler 		return NULL;
18739beb93cSSam Leffler 
18839beb93cSSam Leffler 	if (wps_build_version(msg) ||
18939beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M3) ||
19039beb93cSSam Leffler 	    wps_build_registrar_nonce(wps, msg) ||
19139beb93cSSam Leffler 	    wps_build_e_hash(wps, msg) ||
192f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
19339beb93cSSam Leffler 	    wps_build_authenticator(wps, msg)) {
19439beb93cSSam Leffler 		wpabuf_free(msg);
19539beb93cSSam Leffler 		return NULL;
19639beb93cSSam Leffler 	}
19739beb93cSSam Leffler 
19839beb93cSSam Leffler 	wps->state = RECV_M4;
19939beb93cSSam Leffler 	return msg;
20039beb93cSSam Leffler }
20139beb93cSSam Leffler 
20239beb93cSSam Leffler 
20339beb93cSSam Leffler static struct wpabuf * wps_build_m5(struct wps_data *wps)
20439beb93cSSam Leffler {
20539beb93cSSam Leffler 	struct wpabuf *msg, *plain;
20639beb93cSSam Leffler 
20739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
20839beb93cSSam Leffler 
20939beb93cSSam Leffler 	plain = wpabuf_alloc(200);
21039beb93cSSam Leffler 	if (plain == NULL)
21139beb93cSSam Leffler 		return NULL;
21239beb93cSSam Leffler 
21339beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
21439beb93cSSam Leffler 	if (msg == NULL) {
21539beb93cSSam Leffler 		wpabuf_free(plain);
21639beb93cSSam Leffler 		return NULL;
21739beb93cSSam Leffler 	}
21839beb93cSSam Leffler 
21939beb93cSSam Leffler 	if (wps_build_version(msg) ||
22039beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M5) ||
22139beb93cSSam Leffler 	    wps_build_registrar_nonce(wps, msg) ||
22239beb93cSSam Leffler 	    wps_build_e_snonce1(wps, plain) ||
22339beb93cSSam Leffler 	    wps_build_key_wrap_auth(wps, plain) ||
22439beb93cSSam Leffler 	    wps_build_encr_settings(wps, msg, plain) ||
225f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
22639beb93cSSam Leffler 	    wps_build_authenticator(wps, msg)) {
22739beb93cSSam Leffler 		wpabuf_free(plain);
22839beb93cSSam Leffler 		wpabuf_free(msg);
22939beb93cSSam Leffler 		return NULL;
23039beb93cSSam Leffler 	}
23139beb93cSSam Leffler 	wpabuf_free(plain);
23239beb93cSSam Leffler 
23339beb93cSSam Leffler 	wps->state = RECV_M6;
23439beb93cSSam Leffler 	return msg;
23539beb93cSSam Leffler }
23639beb93cSSam Leffler 
23739beb93cSSam Leffler 
23839beb93cSSam Leffler static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
23939beb93cSSam Leffler {
24039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
24139beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_SSID);
24239beb93cSSam Leffler 	wpabuf_put_be16(msg, wps->wps->ssid_len);
24339beb93cSSam Leffler 	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
24439beb93cSSam Leffler 	return 0;
24539beb93cSSam Leffler }
24639beb93cSSam Leffler 
24739beb93cSSam Leffler 
24839beb93cSSam Leffler static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
24939beb93cSSam Leffler {
250*5b9c547cSRui Paulo 	u16 auth_type = wps->wps->ap_auth_type;
251f05cddf9SRui Paulo 
252*5b9c547cSRui Paulo 	/*
253*5b9c547cSRui Paulo 	 * Work around issues with Windows 7 WPS implementation not liking
254*5b9c547cSRui Paulo 	 * multiple Authentication Type bits in M7 AP Settings attribute by
255*5b9c547cSRui Paulo 	 * showing only the most secure option from current configuration.
256*5b9c547cSRui Paulo 	 */
257f05cddf9SRui Paulo 	if (auth_type & WPS_AUTH_WPA2PSK)
258f05cddf9SRui Paulo 		auth_type = WPS_AUTH_WPA2PSK;
259f05cddf9SRui Paulo 	else if (auth_type & WPS_AUTH_WPAPSK)
260f05cddf9SRui Paulo 		auth_type = WPS_AUTH_WPAPSK;
261f05cddf9SRui Paulo 	else if (auth_type & WPS_AUTH_OPEN)
262f05cddf9SRui Paulo 		auth_type = WPS_AUTH_OPEN;
263f05cddf9SRui Paulo 
264f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
26539beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
26639beb93cSSam Leffler 	wpabuf_put_be16(msg, 2);
267f05cddf9SRui Paulo 	wpabuf_put_be16(msg, auth_type);
26839beb93cSSam Leffler 	return 0;
26939beb93cSSam Leffler }
27039beb93cSSam Leffler 
27139beb93cSSam Leffler 
27239beb93cSSam Leffler static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
27339beb93cSSam Leffler {
274*5b9c547cSRui Paulo 	u16 encr_type = wps->wps->ap_encr_type;
275f05cddf9SRui Paulo 
276*5b9c547cSRui Paulo 	/*
277*5b9c547cSRui Paulo 	 * Work around issues with Windows 7 WPS implementation not liking
278*5b9c547cSRui Paulo 	 * multiple Encryption Type bits in M7 AP Settings attribute by
279*5b9c547cSRui Paulo 	 * showing only the most secure option from current configuration.
280*5b9c547cSRui Paulo 	 */
281*5b9c547cSRui Paulo 	if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
282f05cddf9SRui Paulo 		if (encr_type & WPS_ENCR_AES)
283f05cddf9SRui Paulo 			encr_type = WPS_ENCR_AES;
284f05cddf9SRui Paulo 		else if (encr_type & WPS_ENCR_TKIP)
285f05cddf9SRui Paulo 			encr_type = WPS_ENCR_TKIP;
286f05cddf9SRui Paulo 	}
287f05cddf9SRui Paulo 
288f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
28939beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
29039beb93cSSam Leffler 	wpabuf_put_be16(msg, 2);
291f05cddf9SRui Paulo 	wpabuf_put_be16(msg, encr_type);
29239beb93cSSam Leffler 	return 0;
29339beb93cSSam Leffler }
29439beb93cSSam Leffler 
29539beb93cSSam Leffler 
29639beb93cSSam Leffler static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
29739beb93cSSam Leffler {
298*5b9c547cSRui Paulo 	if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
299*5b9c547cSRui Paulo 	    wps->wps->network_key_len == 0) {
300*5b9c547cSRui Paulo 		char hex[65];
301*5b9c547cSRui Paulo 		u8 psk[32];
302*5b9c547cSRui Paulo 		/* Generate a random per-device PSK */
303*5b9c547cSRui Paulo 		if (random_pool_ready() != 1 ||
304*5b9c547cSRui Paulo 		    random_get_bytes(psk, sizeof(psk)) < 0) {
305*5b9c547cSRui Paulo 			wpa_printf(MSG_INFO,
306*5b9c547cSRui Paulo 				   "WPS: Could not generate random PSK");
307*5b9c547cSRui Paulo 			return -1;
308*5b9c547cSRui Paulo 		}
309*5b9c547cSRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
310*5b9c547cSRui Paulo 				psk, sizeof(psk));
311*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
312*5b9c547cSRui Paulo 			   (unsigned int) wps->new_psk_len * 2);
313*5b9c547cSRui Paulo 		wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
314*5b9c547cSRui Paulo 		wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
315*5b9c547cSRui Paulo 		wpabuf_put_be16(msg, sizeof(psk) * 2);
316*5b9c547cSRui Paulo 		wpabuf_put_data(msg, hex, sizeof(psk) * 2);
317*5b9c547cSRui Paulo 		if (wps->wps->registrar) {
318*5b9c547cSRui Paulo 			wps_cb_new_psk(wps->wps->registrar,
319*5b9c547cSRui Paulo 				       wps->peer_dev.mac_addr,
320*5b9c547cSRui Paulo 				       wps->p2p_dev_addr, psk, sizeof(psk));
321*5b9c547cSRui Paulo 		}
322*5b9c547cSRui Paulo 		return 0;
323*5b9c547cSRui Paulo 	}
324*5b9c547cSRui Paulo 
325*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
326*5b9c547cSRui Paulo 		   (unsigned int) wps->wps->network_key_len);
32739beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
32839beb93cSSam Leffler 	wpabuf_put_be16(msg, wps->wps->network_key_len);
32939beb93cSSam Leffler 	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
33039beb93cSSam Leffler 	return 0;
33139beb93cSSam Leffler }
33239beb93cSSam Leffler 
33339beb93cSSam Leffler 
33439beb93cSSam Leffler static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
33539beb93cSSam Leffler {
33639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
33739beb93cSSam Leffler 	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
33839beb93cSSam Leffler 	wpabuf_put_be16(msg, ETH_ALEN);
33939beb93cSSam Leffler 	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
34039beb93cSSam Leffler 	return 0;
34139beb93cSSam Leffler }
34239beb93cSSam Leffler 
34339beb93cSSam Leffler 
34439beb93cSSam Leffler static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
34539beb93cSSam Leffler {
346*5b9c547cSRui Paulo 	const u8 *start, *end;
347*5b9c547cSRui Paulo 	int ret;
348*5b9c547cSRui Paulo 
34939beb93cSSam Leffler 	if (wps->wps->ap_settings) {
35039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
35139beb93cSSam Leffler 		wpabuf_put_data(plain, wps->wps->ap_settings,
35239beb93cSSam Leffler 				wps->wps->ap_settings_len);
35339beb93cSSam Leffler 		return 0;
35439beb93cSSam Leffler 	}
35539beb93cSSam Leffler 
356*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * AP Settings based on current configuration");
357*5b9c547cSRui Paulo 	start = wpabuf_put(plain, 0);
358*5b9c547cSRui Paulo 	ret = wps_build_cred_ssid(wps, plain) ||
35939beb93cSSam Leffler 		wps_build_cred_mac_addr(wps, plain) ||
36039beb93cSSam Leffler 		wps_build_cred_auth_type(wps, plain) ||
36139beb93cSSam Leffler 		wps_build_cred_encr_type(wps, plain) ||
36239beb93cSSam Leffler 		wps_build_cred_network_key(wps, plain);
363*5b9c547cSRui Paulo 	end = wpabuf_put(plain, 0);
364*5b9c547cSRui Paulo 
365*5b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
366*5b9c547cSRui Paulo 			start, end - start);
367*5b9c547cSRui Paulo 
368*5b9c547cSRui Paulo 	return ret;
36939beb93cSSam Leffler }
37039beb93cSSam Leffler 
37139beb93cSSam Leffler 
37239beb93cSSam Leffler static struct wpabuf * wps_build_m7(struct wps_data *wps)
37339beb93cSSam Leffler {
37439beb93cSSam Leffler 	struct wpabuf *msg, *plain;
37539beb93cSSam Leffler 
37639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
37939beb93cSSam Leffler 	if (plain == NULL)
38039beb93cSSam Leffler 		return NULL;
38139beb93cSSam Leffler 
38239beb93cSSam Leffler 	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
38339beb93cSSam Leffler 	if (msg == NULL) {
38439beb93cSSam Leffler 		wpabuf_free(plain);
38539beb93cSSam Leffler 		return NULL;
38639beb93cSSam Leffler 	}
38739beb93cSSam Leffler 
38839beb93cSSam Leffler 	if (wps_build_version(msg) ||
38939beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_M7) ||
39039beb93cSSam Leffler 	    wps_build_registrar_nonce(wps, msg) ||
39139beb93cSSam Leffler 	    wps_build_e_snonce2(wps, plain) ||
39239beb93cSSam Leffler 	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
39339beb93cSSam Leffler 	    wps_build_key_wrap_auth(wps, plain) ||
39439beb93cSSam Leffler 	    wps_build_encr_settings(wps, msg, plain) ||
395f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
39639beb93cSSam Leffler 	    wps_build_authenticator(wps, msg)) {
39739beb93cSSam Leffler 		wpabuf_free(plain);
39839beb93cSSam Leffler 		wpabuf_free(msg);
39939beb93cSSam Leffler 		return NULL;
40039beb93cSSam Leffler 	}
40139beb93cSSam Leffler 	wpabuf_free(plain);
40239beb93cSSam Leffler 
403e28a4053SRui Paulo 	if (wps->wps->ap && wps->wps->registrar) {
404e28a4053SRui Paulo 		/*
405e28a4053SRui Paulo 		 * If the Registrar is only learning our current configuration,
406e28a4053SRui Paulo 		 * it may not continue protocol run to successful completion.
407e28a4053SRui Paulo 		 * Store information here to make sure it remains available.
408e28a4053SRui Paulo 		 */
409e28a4053SRui Paulo 		wps_device_store(wps->wps->registrar, &wps->peer_dev,
410e28a4053SRui Paulo 				 wps->uuid_r);
411e28a4053SRui Paulo 	}
412e28a4053SRui Paulo 
41339beb93cSSam Leffler 	wps->state = RECV_M8;
41439beb93cSSam Leffler 	return msg;
41539beb93cSSam Leffler }
41639beb93cSSam Leffler 
41739beb93cSSam Leffler 
41839beb93cSSam Leffler static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
41939beb93cSSam Leffler {
42039beb93cSSam Leffler 	struct wpabuf *msg;
42139beb93cSSam Leffler 
42239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
42339beb93cSSam Leffler 
42439beb93cSSam Leffler 	msg = wpabuf_alloc(1000);
42539beb93cSSam Leffler 	if (msg == NULL)
42639beb93cSSam Leffler 		return NULL;
42739beb93cSSam Leffler 
42839beb93cSSam Leffler 	if (wps_build_version(msg) ||
42939beb93cSSam Leffler 	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
43039beb93cSSam Leffler 	    wps_build_enrollee_nonce(wps, msg) ||
431f05cddf9SRui Paulo 	    wps_build_registrar_nonce(wps, msg) ||
432f05cddf9SRui Paulo 	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
43339beb93cSSam Leffler 		wpabuf_free(msg);
43439beb93cSSam Leffler 		return NULL;
43539beb93cSSam Leffler 	}
43639beb93cSSam Leffler 
43739beb93cSSam Leffler 	if (wps->wps->ap)
43839beb93cSSam Leffler 		wps->state = RECV_ACK;
43939beb93cSSam Leffler 	else {
440*5b9c547cSRui Paulo 		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
44139beb93cSSam Leffler 		wps->state = WPS_FINISHED;
44239beb93cSSam Leffler 	}
44339beb93cSSam Leffler 	return msg;
44439beb93cSSam Leffler }
44539beb93cSSam Leffler 
44639beb93cSSam Leffler 
44739beb93cSSam Leffler struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
44839beb93cSSam Leffler 				     enum wsc_op_code *op_code)
44939beb93cSSam Leffler {
45039beb93cSSam Leffler 	struct wpabuf *msg;
45139beb93cSSam Leffler 
45239beb93cSSam Leffler 	switch (wps->state) {
45339beb93cSSam Leffler 	case SEND_M1:
45439beb93cSSam Leffler 		msg = wps_build_m1(wps);
45539beb93cSSam Leffler 		*op_code = WSC_MSG;
45639beb93cSSam Leffler 		break;
45739beb93cSSam Leffler 	case SEND_M3:
45839beb93cSSam Leffler 		msg = wps_build_m3(wps);
45939beb93cSSam Leffler 		*op_code = WSC_MSG;
46039beb93cSSam Leffler 		break;
46139beb93cSSam Leffler 	case SEND_M5:
46239beb93cSSam Leffler 		msg = wps_build_m5(wps);
46339beb93cSSam Leffler 		*op_code = WSC_MSG;
46439beb93cSSam Leffler 		break;
46539beb93cSSam Leffler 	case SEND_M7:
46639beb93cSSam Leffler 		msg = wps_build_m7(wps);
46739beb93cSSam Leffler 		*op_code = WSC_MSG;
46839beb93cSSam Leffler 		break;
46939beb93cSSam Leffler 	case RECEIVED_M2D:
47039beb93cSSam Leffler 		if (wps->wps->ap) {
47139beb93cSSam Leffler 			msg = wps_build_wsc_nack(wps);
47239beb93cSSam Leffler 			*op_code = WSC_NACK;
47339beb93cSSam Leffler 			break;
47439beb93cSSam Leffler 		}
47539beb93cSSam Leffler 		msg = wps_build_wsc_ack(wps);
47639beb93cSSam Leffler 		*op_code = WSC_ACK;
47739beb93cSSam Leffler 		if (msg) {
47839beb93cSSam Leffler 			/* Another M2/M2D may be received */
47939beb93cSSam Leffler 			wps->state = RECV_M2;
48039beb93cSSam Leffler 		}
48139beb93cSSam Leffler 		break;
48239beb93cSSam Leffler 	case SEND_WSC_NACK:
48339beb93cSSam Leffler 		msg = wps_build_wsc_nack(wps);
48439beb93cSSam Leffler 		*op_code = WSC_NACK;
48539beb93cSSam Leffler 		break;
48639beb93cSSam Leffler 	case WPS_MSG_DONE:
48739beb93cSSam Leffler 		msg = wps_build_wsc_done(wps);
48839beb93cSSam Leffler 		*op_code = WSC_Done;
48939beb93cSSam Leffler 		break;
49039beb93cSSam Leffler 	default:
49139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
49239beb93cSSam Leffler 			   "a message", wps->state);
49339beb93cSSam Leffler 		msg = NULL;
49439beb93cSSam Leffler 		break;
49539beb93cSSam Leffler 	}
49639beb93cSSam Leffler 
49739beb93cSSam Leffler 	if (*op_code == WSC_MSG && msg) {
49839beb93cSSam Leffler 		/* Save a copy of the last message for Authenticator derivation
49939beb93cSSam Leffler 		 */
50039beb93cSSam Leffler 		wpabuf_free(wps->last_msg);
50139beb93cSSam Leffler 		wps->last_msg = wpabuf_dup(msg);
50239beb93cSSam Leffler 	}
50339beb93cSSam Leffler 
50439beb93cSSam Leffler 	return msg;
50539beb93cSSam Leffler }
50639beb93cSSam Leffler 
50739beb93cSSam Leffler 
50839beb93cSSam Leffler static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
50939beb93cSSam Leffler {
51039beb93cSSam Leffler 	if (r_nonce == NULL) {
51139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
51239beb93cSSam Leffler 		return -1;
51339beb93cSSam Leffler 	}
51439beb93cSSam Leffler 
51539beb93cSSam Leffler 	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
51639beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
51739beb93cSSam Leffler 		    wps->nonce_r, WPS_NONCE_LEN);
51839beb93cSSam Leffler 
51939beb93cSSam Leffler 	return 0;
52039beb93cSSam Leffler }
52139beb93cSSam Leffler 
52239beb93cSSam Leffler 
52339beb93cSSam Leffler static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
52439beb93cSSam Leffler {
52539beb93cSSam Leffler 	if (e_nonce == NULL) {
52639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
52739beb93cSSam Leffler 		return -1;
52839beb93cSSam Leffler 	}
52939beb93cSSam Leffler 
53039beb93cSSam Leffler 	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
53139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
53239beb93cSSam Leffler 		return -1;
53339beb93cSSam Leffler 	}
53439beb93cSSam Leffler 
53539beb93cSSam Leffler 	return 0;
53639beb93cSSam Leffler }
53739beb93cSSam Leffler 
53839beb93cSSam Leffler 
53939beb93cSSam Leffler static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
54039beb93cSSam Leffler {
54139beb93cSSam Leffler 	if (uuid_r == NULL) {
54239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
54339beb93cSSam Leffler 		return -1;
54439beb93cSSam Leffler 	}
54539beb93cSSam Leffler 
54639beb93cSSam Leffler 	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
54739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
54839beb93cSSam Leffler 
54939beb93cSSam Leffler 	return 0;
55039beb93cSSam Leffler }
55139beb93cSSam Leffler 
55239beb93cSSam Leffler 
55339beb93cSSam Leffler static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
55439beb93cSSam Leffler 			      size_t pk_len)
55539beb93cSSam Leffler {
55639beb93cSSam Leffler 	if (pk == NULL || pk_len == 0) {
55739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
55839beb93cSSam Leffler 		return -1;
55939beb93cSSam Leffler 	}
56039beb93cSSam Leffler 
561*5b9c547cSRui Paulo 	if (wps->peer_pubkey_hash_set) {
562*5b9c547cSRui Paulo 		u8 hash[WPS_HASH_LEN];
563*5b9c547cSRui Paulo 		sha256_vector(1, &pk, &pk_len, hash);
564*5b9c547cSRui Paulo 		if (os_memcmp_const(hash, wps->peer_pubkey_hash,
565*5b9c547cSRui Paulo 				    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
566*5b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
567*5b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
568*5b9c547cSRui Paulo 				    pk, pk_len);
569*5b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
570*5b9c547cSRui Paulo 				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
571*5b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
572*5b9c547cSRui Paulo 				    wps->peer_pubkey_hash,
573*5b9c547cSRui Paulo 				    WPS_OOB_PUBKEY_HASH_LEN);
574*5b9c547cSRui Paulo 			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
575*5b9c547cSRui Paulo 			return -1;
576*5b9c547cSRui Paulo 		}
577*5b9c547cSRui Paulo 	}
578*5b9c547cSRui Paulo 
57939beb93cSSam Leffler 	wpabuf_free(wps->dh_pubkey_r);
58039beb93cSSam Leffler 	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
58139beb93cSSam Leffler 	if (wps->dh_pubkey_r == NULL)
58239beb93cSSam Leffler 		return -1;
58339beb93cSSam Leffler 
58439beb93cSSam Leffler 	if (wps_derive_keys(wps) < 0)
58539beb93cSSam Leffler 		return -1;
58639beb93cSSam Leffler 
58739beb93cSSam Leffler 	return 0;
58839beb93cSSam Leffler }
58939beb93cSSam Leffler 
59039beb93cSSam Leffler 
59139beb93cSSam Leffler static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
59239beb93cSSam Leffler {
59339beb93cSSam Leffler 	if (r_hash1 == NULL) {
59439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
59539beb93cSSam Leffler 		return -1;
59639beb93cSSam Leffler 	}
59739beb93cSSam Leffler 
59839beb93cSSam Leffler 	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
59939beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
60039beb93cSSam Leffler 
60139beb93cSSam Leffler 	return 0;
60239beb93cSSam Leffler }
60339beb93cSSam Leffler 
60439beb93cSSam Leffler 
60539beb93cSSam Leffler static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
60639beb93cSSam Leffler {
60739beb93cSSam Leffler 	if (r_hash2 == NULL) {
60839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
60939beb93cSSam Leffler 		return -1;
61039beb93cSSam Leffler 	}
61139beb93cSSam Leffler 
61239beb93cSSam Leffler 	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
61339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
61439beb93cSSam Leffler 
61539beb93cSSam Leffler 	return 0;
61639beb93cSSam Leffler }
61739beb93cSSam Leffler 
61839beb93cSSam Leffler 
61939beb93cSSam Leffler static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
62039beb93cSSam Leffler {
62139beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
62239beb93cSSam Leffler 	const u8 *addr[4];
62339beb93cSSam Leffler 	size_t len[4];
62439beb93cSSam Leffler 
62539beb93cSSam Leffler 	if (r_snonce1 == NULL) {
62639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
62739beb93cSSam Leffler 		return -1;
62839beb93cSSam Leffler 	}
62939beb93cSSam Leffler 
63039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
63139beb93cSSam Leffler 			WPS_SECRET_NONCE_LEN);
63239beb93cSSam Leffler 
63339beb93cSSam Leffler 	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
63439beb93cSSam Leffler 	addr[0] = r_snonce1;
63539beb93cSSam Leffler 	len[0] = WPS_SECRET_NONCE_LEN;
63639beb93cSSam Leffler 	addr[1] = wps->psk1;
63739beb93cSSam Leffler 	len[1] = WPS_PSK_LEN;
63839beb93cSSam Leffler 	addr[2] = wpabuf_head(wps->dh_pubkey_e);
63939beb93cSSam Leffler 	len[2] = wpabuf_len(wps->dh_pubkey_e);
64039beb93cSSam Leffler 	addr[3] = wpabuf_head(wps->dh_pubkey_r);
64139beb93cSSam Leffler 	len[3] = wpabuf_len(wps->dh_pubkey_r);
64239beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
64339beb93cSSam Leffler 
644*5b9c547cSRui Paulo 	if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
64539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
64639beb93cSSam Leffler 			   "not match with the pre-committed value");
64739beb93cSSam Leffler 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
648*5b9c547cSRui Paulo 		wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr);
64939beb93cSSam Leffler 		return -1;
65039beb93cSSam Leffler 	}
65139beb93cSSam Leffler 
65239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
65339beb93cSSam Leffler 		   "half of the device password");
65439beb93cSSam Leffler 
65539beb93cSSam Leffler 	return 0;
65639beb93cSSam Leffler }
65739beb93cSSam Leffler 
65839beb93cSSam Leffler 
65939beb93cSSam Leffler static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
66039beb93cSSam Leffler {
66139beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
66239beb93cSSam Leffler 	const u8 *addr[4];
66339beb93cSSam Leffler 	size_t len[4];
66439beb93cSSam Leffler 
66539beb93cSSam Leffler 	if (r_snonce2 == NULL) {
66639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
66739beb93cSSam Leffler 		return -1;
66839beb93cSSam Leffler 	}
66939beb93cSSam Leffler 
67039beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
67139beb93cSSam Leffler 			WPS_SECRET_NONCE_LEN);
67239beb93cSSam Leffler 
67339beb93cSSam Leffler 	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
67439beb93cSSam Leffler 	addr[0] = r_snonce2;
67539beb93cSSam Leffler 	len[0] = WPS_SECRET_NONCE_LEN;
67639beb93cSSam Leffler 	addr[1] = wps->psk2;
67739beb93cSSam Leffler 	len[1] = WPS_PSK_LEN;
67839beb93cSSam Leffler 	addr[2] = wpabuf_head(wps->dh_pubkey_e);
67939beb93cSSam Leffler 	len[2] = wpabuf_len(wps->dh_pubkey_e);
68039beb93cSSam Leffler 	addr[3] = wpabuf_head(wps->dh_pubkey_r);
68139beb93cSSam Leffler 	len[3] = wpabuf_len(wps->dh_pubkey_r);
68239beb93cSSam Leffler 	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
68339beb93cSSam Leffler 
684*5b9c547cSRui Paulo 	if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
68539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
68639beb93cSSam Leffler 			   "not match with the pre-committed value");
68739beb93cSSam Leffler 		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
688*5b9c547cSRui Paulo 		wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr);
68939beb93cSSam Leffler 		return -1;
69039beb93cSSam Leffler 	}
69139beb93cSSam Leffler 
69239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
69339beb93cSSam Leffler 		   "half of the device password");
69439beb93cSSam Leffler 
69539beb93cSSam Leffler 	return 0;
69639beb93cSSam Leffler }
69739beb93cSSam Leffler 
69839beb93cSSam Leffler 
69939beb93cSSam Leffler static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
700f05cddf9SRui Paulo 			      size_t cred_len, int wps2)
70139beb93cSSam Leffler {
70239beb93cSSam Leffler 	struct wps_parse_attr attr;
70339beb93cSSam Leffler 	struct wpabuf msg;
704f05cddf9SRui Paulo 	int ret = 0;
70539beb93cSSam Leffler 
70639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
70739beb93cSSam Leffler 	os_memset(&wps->cred, 0, sizeof(wps->cred));
70839beb93cSSam Leffler 	wpabuf_set(&msg, cred, cred_len);
70939beb93cSSam Leffler 	if (wps_parse_msg(&msg, &attr) < 0 ||
71039beb93cSSam Leffler 	    wps_process_cred(&attr, &wps->cred))
71139beb93cSSam Leffler 		return -1;
71239beb93cSSam Leffler 
7133157ba21SRui Paulo 	if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
7143157ba21SRui Paulo 	    0) {
7153157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
7163157ba21SRui Paulo 			   MACSTR ") does not match with own address (" MACSTR
7173157ba21SRui Paulo 			   ")", MAC2STR(wps->cred.mac_addr),
7183157ba21SRui Paulo 			   MAC2STR(wps->wps->dev.mac_addr));
7193157ba21SRui Paulo 		/*
7203157ba21SRui Paulo 		 * In theory, this could be consider fatal error, but there are
7213157ba21SRui Paulo 		 * number of deployed implementations using other address here
7223157ba21SRui Paulo 		 * due to unclarity in the specification. For interoperability
7233157ba21SRui Paulo 		 * reasons, allow this to be processed since we do not really
7243157ba21SRui Paulo 		 * use the MAC Address information for anything.
7253157ba21SRui Paulo 		 */
726f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
727f05cddf9SRui Paulo 		if (wps2) {
728f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
729f05cddf9SRui Paulo 				   "MAC Address in AP Settings");
730f05cddf9SRui Paulo 			return -1;
7313157ba21SRui Paulo 		}
732f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
733f05cddf9SRui Paulo 	}
734f05cddf9SRui Paulo 
735f05cddf9SRui Paulo 	if (!(wps->cred.encr_type &
736f05cddf9SRui Paulo 	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
737f05cddf9SRui Paulo 		if (wps->cred.encr_type & WPS_ENCR_WEP) {
738f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Reject Credential "
739f05cddf9SRui Paulo 				   "due to WEP configuration");
740f05cddf9SRui Paulo 			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
741f05cddf9SRui Paulo 			return -2;
742f05cddf9SRui Paulo 		}
743f05cddf9SRui Paulo 
744f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
745f05cddf9SRui Paulo 			   "invalid encr_type 0x%x", wps->cred.encr_type);
746f05cddf9SRui Paulo 		return -1;
747f05cddf9SRui Paulo 	}
7483157ba21SRui Paulo 
74939beb93cSSam Leffler 	if (wps->wps->cred_cb) {
75039beb93cSSam Leffler 		wps->cred.cred_attr = cred - 4;
75139beb93cSSam Leffler 		wps->cred.cred_attr_len = cred_len + 4;
752f05cddf9SRui Paulo 		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
75339beb93cSSam Leffler 		wps->cred.cred_attr = NULL;
75439beb93cSSam Leffler 		wps->cred.cred_attr_len = 0;
75539beb93cSSam Leffler 	}
75639beb93cSSam Leffler 
757f05cddf9SRui Paulo 	return ret;
75839beb93cSSam Leffler }
75939beb93cSSam Leffler 
76039beb93cSSam Leffler 
76139beb93cSSam Leffler static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
762f05cddf9SRui Paulo 			     size_t cred_len[], size_t num_cred, int wps2)
76339beb93cSSam Leffler {
76439beb93cSSam Leffler 	size_t i;
765f05cddf9SRui Paulo 	int ok = 0;
76639beb93cSSam Leffler 
76739beb93cSSam Leffler 	if (wps->wps->ap)
76839beb93cSSam Leffler 		return 0;
76939beb93cSSam Leffler 
77039beb93cSSam Leffler 	if (num_cred == 0) {
77139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
77239beb93cSSam Leffler 			   "received");
77339beb93cSSam Leffler 		return -1;
77439beb93cSSam Leffler 	}
77539beb93cSSam Leffler 
77639beb93cSSam Leffler 	for (i = 0; i < num_cred; i++) {
777f05cddf9SRui Paulo 		int res;
778f05cddf9SRui Paulo 		res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
779f05cddf9SRui Paulo 		if (res == 0)
780f05cddf9SRui Paulo 			ok++;
781f05cddf9SRui Paulo 		else if (res == -2)
782f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
783f05cddf9SRui Paulo 		else
784f05cddf9SRui Paulo 			return -1;
785f05cddf9SRui Paulo 	}
786f05cddf9SRui Paulo 
787f05cddf9SRui Paulo 	if (ok == 0) {
788f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
789f05cddf9SRui Paulo 			   "received");
79039beb93cSSam Leffler 		return -1;
79139beb93cSSam Leffler 	}
79239beb93cSSam Leffler 
79339beb93cSSam Leffler 	return 0;
79439beb93cSSam Leffler }
79539beb93cSSam Leffler 
79639beb93cSSam Leffler 
79739beb93cSSam Leffler static int wps_process_ap_settings_e(struct wps_data *wps,
79839beb93cSSam Leffler 				     struct wps_parse_attr *attr,
799f05cddf9SRui Paulo 				     struct wpabuf *attrs, int wps2)
80039beb93cSSam Leffler {
80139beb93cSSam Leffler 	struct wps_credential cred;
80239beb93cSSam Leffler 
80339beb93cSSam Leffler 	if (!wps->wps->ap)
80439beb93cSSam Leffler 		return 0;
80539beb93cSSam Leffler 
80639beb93cSSam Leffler 	if (wps_process_ap_settings(attr, &cred) < 0)
80739beb93cSSam Leffler 		return -1;
80839beb93cSSam Leffler 
80939beb93cSSam Leffler 	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
81039beb93cSSam Leffler 		   "Registrar");
81139beb93cSSam Leffler 
8123157ba21SRui Paulo 	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
8133157ba21SRui Paulo 	    0) {
8143157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
8153157ba21SRui Paulo 			   MACSTR ") does not match with own address (" MACSTR
8163157ba21SRui Paulo 			   ")", MAC2STR(cred.mac_addr),
8173157ba21SRui Paulo 			   MAC2STR(wps->wps->dev.mac_addr));
8183157ba21SRui Paulo 		/*
8193157ba21SRui Paulo 		 * In theory, this could be consider fatal error, but there are
8203157ba21SRui Paulo 		 * number of deployed implementations using other address here
8213157ba21SRui Paulo 		 * due to unclarity in the specification. For interoperability
8223157ba21SRui Paulo 		 * reasons, allow this to be processed since we do not really
8233157ba21SRui Paulo 		 * use the MAC Address information for anything.
8243157ba21SRui Paulo 		 */
825f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
826f05cddf9SRui Paulo 		if (wps2) {
827f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
828f05cddf9SRui Paulo 				   "MAC Address in AP Settings");
829f05cddf9SRui Paulo 			return -1;
8303157ba21SRui Paulo 		}
831f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
832f05cddf9SRui Paulo 	}
833f05cddf9SRui Paulo 
834f05cddf9SRui Paulo 	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
835f05cddf9SRui Paulo 	{
836f05cddf9SRui Paulo 		if (cred.encr_type & WPS_ENCR_WEP) {
837f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
838f05cddf9SRui Paulo 				   "due to WEP configuration");
839f05cddf9SRui Paulo 			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
840f05cddf9SRui Paulo 			return -1;
841f05cddf9SRui Paulo 		}
842f05cddf9SRui Paulo 
843f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
844f05cddf9SRui Paulo 			   "invalid encr_type 0x%x", cred.encr_type);
845f05cddf9SRui Paulo 		return -1;
846f05cddf9SRui Paulo 	}
847f05cddf9SRui Paulo 
848f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
849f05cddf9SRui Paulo 	if (wps2) {
850f05cddf9SRui Paulo 		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
851f05cddf9SRui Paulo 		    WPS_ENCR_TKIP ||
852f05cddf9SRui Paulo 		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
853f05cddf9SRui Paulo 		    WPS_AUTH_WPAPSK) {
854f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
855f05cddf9SRui Paulo 				   "AP Settings: WPA-Personal/TKIP only");
856f05cddf9SRui Paulo 			wps->error_indication =
857f05cddf9SRui Paulo 				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
858f05cddf9SRui Paulo 			return -1;
859f05cddf9SRui Paulo 		}
860f05cddf9SRui Paulo 	}
861f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
862f05cddf9SRui Paulo 
863f05cddf9SRui Paulo 	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
864f05cddf9SRui Paulo 	{
865f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
866f05cddf9SRui Paulo 			   "TKIP+AES");
867f05cddf9SRui Paulo 		cred.encr_type |= WPS_ENCR_AES;
868f05cddf9SRui Paulo 	}
869f05cddf9SRui Paulo 
870f05cddf9SRui Paulo 	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
871f05cddf9SRui Paulo 	    WPS_AUTH_WPAPSK) {
872f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
873f05cddf9SRui Paulo 			   "WPAPSK+WPA2PSK");
874f05cddf9SRui Paulo 		cred.auth_type |= WPS_AUTH_WPA2PSK;
875f05cddf9SRui Paulo 	}
8763157ba21SRui Paulo 
87739beb93cSSam Leffler 	if (wps->wps->cred_cb) {
87839beb93cSSam Leffler 		cred.cred_attr = wpabuf_head(attrs);
87939beb93cSSam Leffler 		cred.cred_attr_len = wpabuf_len(attrs);
88039beb93cSSam Leffler 		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
88139beb93cSSam Leffler 	}
88239beb93cSSam Leffler 
88339beb93cSSam Leffler 	return 0;
88439beb93cSSam Leffler }
88539beb93cSSam Leffler 
88639beb93cSSam Leffler 
887*5b9c547cSRui Paulo static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
888*5b9c547cSRui Paulo {
889*5b9c547cSRui Paulo 	u16 id;
890*5b9c547cSRui Paulo 
891*5b9c547cSRui Paulo 	if (dev_pw_id == NULL) {
892*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
893*5b9c547cSRui Paulo 		return -1;
894*5b9c547cSRui Paulo 	}
895*5b9c547cSRui Paulo 
896*5b9c547cSRui Paulo 	id = WPA_GET_BE16(dev_pw_id);
897*5b9c547cSRui Paulo 	if (wps->dev_pw_id == id) {
898*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
899*5b9c547cSRui Paulo 		return 0;
900*5b9c547cSRui Paulo 	}
901*5b9c547cSRui Paulo 
902*5b9c547cSRui Paulo #ifdef CONFIG_P2P
903*5b9c547cSRui Paulo 	if ((id == DEV_PW_DEFAULT &&
904*5b9c547cSRui Paulo 	     wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
905*5b9c547cSRui Paulo 	    (id == DEV_PW_REGISTRAR_SPECIFIED &&
906*5b9c547cSRui Paulo 	     wps->dev_pw_id == DEV_PW_DEFAULT)) {
907*5b9c547cSRui Paulo 		/*
908*5b9c547cSRui Paulo 		 * Common P2P use cases indicate whether the PIN is from the
909*5b9c547cSRui Paulo 		 * client or GO using Device Password Id in M1/M2 in a way that
910*5b9c547cSRui Paulo 		 * does not look fully compliant with WSC specification. Anyway,
911*5b9c547cSRui Paulo 		 * this is deployed and needs to be allowed, so ignore changes
912*5b9c547cSRui Paulo 		 * between Registrar-Specified and Default PIN.
913*5b9c547cSRui Paulo 		 */
914*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
915*5b9c547cSRui Paulo 			   "change");
916*5b9c547cSRui Paulo 		return 0;
917*5b9c547cSRui Paulo 	}
918*5b9c547cSRui Paulo #endif /* CONFIG_P2P */
919*5b9c547cSRui Paulo 
920*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
921*5b9c547cSRui Paulo 		   "ID from %u to %u", wps->dev_pw_id, id);
922*5b9c547cSRui Paulo 
923*5b9c547cSRui Paulo 	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
924*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
925*5b9c547cSRui Paulo 			   "WPS: Workaround - ignore PBC-to-PIN change");
926*5b9c547cSRui Paulo 		return 0;
927*5b9c547cSRui Paulo 	}
928*5b9c547cSRui Paulo 
929*5b9c547cSRui Paulo 	if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
930*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
931*5b9c547cSRui Paulo 		bin_clear_free(wps->dev_password, wps->dev_password_len);
932*5b9c547cSRui Paulo 		wps->dev_pw_id = wps->alt_dev_pw_id;
933*5b9c547cSRui Paulo 		wps->dev_password = wps->alt_dev_password;
934*5b9c547cSRui Paulo 		wps->dev_password_len = wps->alt_dev_password_len;
935*5b9c547cSRui Paulo 		wps->alt_dev_password = NULL;
936*5b9c547cSRui Paulo 		wps->alt_dev_password_len = 0;
937*5b9c547cSRui Paulo 		return 0;
938*5b9c547cSRui Paulo 	}
939*5b9c547cSRui Paulo 
940*5b9c547cSRui Paulo 	return -1;
941*5b9c547cSRui Paulo }
942*5b9c547cSRui Paulo 
943*5b9c547cSRui Paulo 
94439beb93cSSam Leffler static enum wps_process_res wps_process_m2(struct wps_data *wps,
94539beb93cSSam Leffler 					   const struct wpabuf *msg,
94639beb93cSSam Leffler 					   struct wps_parse_attr *attr)
94739beb93cSSam Leffler {
94839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M2");
94939beb93cSSam Leffler 
95039beb93cSSam Leffler 	if (wps->state != RECV_M2) {
95139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
95239beb93cSSam Leffler 			   "receiving M2", wps->state);
95339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
95439beb93cSSam Leffler 		return WPS_CONTINUE;
95539beb93cSSam Leffler 	}
95639beb93cSSam Leffler 
95739beb93cSSam Leffler 	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
95839beb93cSSam Leffler 	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
959*5b9c547cSRui Paulo 	    wps_process_uuid_r(wps, attr->uuid_r) ||
960*5b9c547cSRui Paulo 	    wps_process_dev_pw_id(wps, attr->dev_password_id)) {
96139beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
96239beb93cSSam Leffler 		return WPS_CONTINUE;
96339beb93cSSam Leffler 	}
96439beb93cSSam Leffler 
965f05cddf9SRui Paulo 	/*
966f05cddf9SRui Paulo 	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
967f05cddf9SRui Paulo 	 * special locked mode is used to allow protocol run up to M7 in order
968f05cddf9SRui Paulo 	 * to support external Registrars that only learn the current AP
969f05cddf9SRui Paulo 	 * configuration without changing it.
970f05cddf9SRui Paulo 	 */
971e28a4053SRui Paulo 	if (wps->wps->ap &&
972f05cddf9SRui Paulo 	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
973f05cddf9SRui Paulo 	     wps->dev_password == NULL)) {
97439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
97539beb93cSSam Leffler 			   "registration of a new Registrar");
97639beb93cSSam Leffler 		wps->config_error = WPS_CFG_SETUP_LOCKED;
97739beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
97839beb93cSSam Leffler 		return WPS_CONTINUE;
97939beb93cSSam Leffler 	}
98039beb93cSSam Leffler 
981e28a4053SRui Paulo 	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
982e28a4053SRui Paulo 	    wps_process_authenticator(wps, attr->authenticator, msg) ||
983e28a4053SRui Paulo 	    wps_process_device_attrs(&wps->peer_dev, attr)) {
984e28a4053SRui Paulo 		wps->state = SEND_WSC_NACK;
985e28a4053SRui Paulo 		return WPS_CONTINUE;
986e28a4053SRui Paulo 	}
987e28a4053SRui Paulo 
988*5b9c547cSRui Paulo #ifdef CONFIG_WPS_NFC
989*5b9c547cSRui Paulo 	if (wps->peer_pubkey_hash_set) {
990*5b9c547cSRui Paulo 		struct wpabuf *decrypted;
991*5b9c547cSRui Paulo 		struct wps_parse_attr eattr;
992*5b9c547cSRui Paulo 
993*5b9c547cSRui Paulo 		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
994*5b9c547cSRui Paulo 						      attr->encr_settings_len);
995*5b9c547cSRui Paulo 		if (decrypted == NULL) {
996*5b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
997*5b9c547cSRui Paulo 				   "Encrypted Settings attribute");
998*5b9c547cSRui Paulo 			wps->state = SEND_WSC_NACK;
999*5b9c547cSRui Paulo 			return WPS_CONTINUE;
1000*5b9c547cSRui Paulo 		}
1001*5b9c547cSRui Paulo 
1002*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
1003*5b9c547cSRui Paulo 			   "Settings attribute");
1004*5b9c547cSRui Paulo 		if (wps_parse_msg(decrypted, &eattr) < 0 ||
1005*5b9c547cSRui Paulo 		    wps_process_key_wrap_auth(wps, decrypted,
1006*5b9c547cSRui Paulo 					      eattr.key_wrap_auth) ||
1007*5b9c547cSRui Paulo 		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1008*5b9c547cSRui Paulo 				      eattr.num_cred, attr->version2 != NULL)) {
1009*5b9c547cSRui Paulo 			wpabuf_free(decrypted);
1010*5b9c547cSRui Paulo 			wps->state = SEND_WSC_NACK;
1011*5b9c547cSRui Paulo 			return WPS_CONTINUE;
1012*5b9c547cSRui Paulo 		}
1013*5b9c547cSRui Paulo 		wpabuf_free(decrypted);
1014*5b9c547cSRui Paulo 
1015*5b9c547cSRui Paulo 		wps->state = WPS_MSG_DONE;
1016*5b9c547cSRui Paulo 		return WPS_CONTINUE;
1017*5b9c547cSRui Paulo 	}
1018*5b9c547cSRui Paulo #endif /* CONFIG_WPS_NFC */
1019*5b9c547cSRui Paulo 
102039beb93cSSam Leffler 	wps->state = SEND_M3;
102139beb93cSSam Leffler 	return WPS_CONTINUE;
102239beb93cSSam Leffler }
102339beb93cSSam Leffler 
102439beb93cSSam Leffler 
102539beb93cSSam Leffler static enum wps_process_res wps_process_m2d(struct wps_data *wps,
102639beb93cSSam Leffler 					    struct wps_parse_attr *attr)
102739beb93cSSam Leffler {
102839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
102939beb93cSSam Leffler 
103039beb93cSSam Leffler 	if (wps->state != RECV_M2) {
103139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
103239beb93cSSam Leffler 			   "receiving M2D", wps->state);
103339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
103439beb93cSSam Leffler 		return WPS_CONTINUE;
103539beb93cSSam Leffler 	}
103639beb93cSSam Leffler 
103739beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
103839beb93cSSam Leffler 			  attr->manufacturer, attr->manufacturer_len);
103939beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
104039beb93cSSam Leffler 			  attr->model_name, attr->model_name_len);
104139beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
104239beb93cSSam Leffler 			  attr->model_number, attr->model_number_len);
104339beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
104439beb93cSSam Leffler 			  attr->serial_number, attr->serial_number_len);
104539beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
104639beb93cSSam Leffler 			  attr->dev_name, attr->dev_name_len);
104739beb93cSSam Leffler 
104839beb93cSSam Leffler 	if (wps->wps->event_cb) {
104939beb93cSSam Leffler 		union wps_event_data data;
105039beb93cSSam Leffler 		struct wps_event_m2d *m2d = &data.m2d;
105139beb93cSSam Leffler 		os_memset(&data, 0, sizeof(data));
105239beb93cSSam Leffler 		if (attr->config_methods)
105339beb93cSSam Leffler 			m2d->config_methods =
105439beb93cSSam Leffler 				WPA_GET_BE16(attr->config_methods);
105539beb93cSSam Leffler 		m2d->manufacturer = attr->manufacturer;
105639beb93cSSam Leffler 		m2d->manufacturer_len = attr->manufacturer_len;
105739beb93cSSam Leffler 		m2d->model_name = attr->model_name;
105839beb93cSSam Leffler 		m2d->model_name_len = attr->model_name_len;
105939beb93cSSam Leffler 		m2d->model_number = attr->model_number;
106039beb93cSSam Leffler 		m2d->model_number_len = attr->model_number_len;
106139beb93cSSam Leffler 		m2d->serial_number = attr->serial_number;
106239beb93cSSam Leffler 		m2d->serial_number_len = attr->serial_number_len;
106339beb93cSSam Leffler 		m2d->dev_name = attr->dev_name;
106439beb93cSSam Leffler 		m2d->dev_name_len = attr->dev_name_len;
106539beb93cSSam Leffler 		m2d->primary_dev_type = attr->primary_dev_type;
106639beb93cSSam Leffler 		if (attr->config_error)
106739beb93cSSam Leffler 			m2d->config_error =
106839beb93cSSam Leffler 				WPA_GET_BE16(attr->config_error);
106939beb93cSSam Leffler 		if (attr->dev_password_id)
107039beb93cSSam Leffler 			m2d->dev_password_id =
107139beb93cSSam Leffler 				WPA_GET_BE16(attr->dev_password_id);
107239beb93cSSam Leffler 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
107339beb93cSSam Leffler 	}
107439beb93cSSam Leffler 
107539beb93cSSam Leffler 	wps->state = RECEIVED_M2D;
107639beb93cSSam Leffler 	return WPS_CONTINUE;
107739beb93cSSam Leffler }
107839beb93cSSam Leffler 
107939beb93cSSam Leffler 
108039beb93cSSam Leffler static enum wps_process_res wps_process_m4(struct wps_data *wps,
108139beb93cSSam Leffler 					   const struct wpabuf *msg,
108239beb93cSSam Leffler 					   struct wps_parse_attr *attr)
108339beb93cSSam Leffler {
108439beb93cSSam Leffler 	struct wpabuf *decrypted;
108539beb93cSSam Leffler 	struct wps_parse_attr eattr;
108639beb93cSSam Leffler 
108739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M4");
108839beb93cSSam Leffler 
108939beb93cSSam Leffler 	if (wps->state != RECV_M4) {
109039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
109139beb93cSSam Leffler 			   "receiving M4", wps->state);
109239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
109339beb93cSSam Leffler 		return WPS_CONTINUE;
109439beb93cSSam Leffler 	}
109539beb93cSSam Leffler 
109639beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
109739beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg) ||
109839beb93cSSam Leffler 	    wps_process_r_hash1(wps, attr->r_hash1) ||
109939beb93cSSam Leffler 	    wps_process_r_hash2(wps, attr->r_hash2)) {
110039beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
110139beb93cSSam Leffler 		return WPS_CONTINUE;
110239beb93cSSam Leffler 	}
110339beb93cSSam Leffler 
110439beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
110539beb93cSSam Leffler 					      attr->encr_settings_len);
110639beb93cSSam Leffler 	if (decrypted == NULL) {
110739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
110839beb93cSSam Leffler 			   "Settings attribute");
110939beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
111039beb93cSSam Leffler 		return WPS_CONTINUE;
111139beb93cSSam Leffler 	}
111239beb93cSSam Leffler 
1113f05cddf9SRui Paulo 	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
1114f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1115f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1116f05cddf9SRui Paulo 		return WPS_CONTINUE;
1117f05cddf9SRui Paulo 	}
1118f05cddf9SRui Paulo 
111939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
112039beb93cSSam Leffler 		   "attribute");
112139beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
112239beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
112339beb93cSSam Leffler 	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
112439beb93cSSam Leffler 		wpabuf_free(decrypted);
112539beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
112639beb93cSSam Leffler 		return WPS_CONTINUE;
112739beb93cSSam Leffler 	}
112839beb93cSSam Leffler 	wpabuf_free(decrypted);
112939beb93cSSam Leffler 
113039beb93cSSam Leffler 	wps->state = SEND_M5;
113139beb93cSSam Leffler 	return WPS_CONTINUE;
113239beb93cSSam Leffler }
113339beb93cSSam Leffler 
113439beb93cSSam Leffler 
113539beb93cSSam Leffler static enum wps_process_res wps_process_m6(struct wps_data *wps,
113639beb93cSSam Leffler 					   const struct wpabuf *msg,
113739beb93cSSam Leffler 					   struct wps_parse_attr *attr)
113839beb93cSSam Leffler {
113939beb93cSSam Leffler 	struct wpabuf *decrypted;
114039beb93cSSam Leffler 	struct wps_parse_attr eattr;
114139beb93cSSam Leffler 
114239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M6");
114339beb93cSSam Leffler 
114439beb93cSSam Leffler 	if (wps->state != RECV_M6) {
114539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
114639beb93cSSam Leffler 			   "receiving M6", wps->state);
114739beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
114839beb93cSSam Leffler 		return WPS_CONTINUE;
114939beb93cSSam Leffler 	}
115039beb93cSSam Leffler 
115139beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
115239beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg)) {
115339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
115439beb93cSSam Leffler 		return WPS_CONTINUE;
115539beb93cSSam Leffler 	}
115639beb93cSSam Leffler 
115739beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
115839beb93cSSam Leffler 					      attr->encr_settings_len);
115939beb93cSSam Leffler 	if (decrypted == NULL) {
116039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
116139beb93cSSam Leffler 			   "Settings attribute");
116239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
116339beb93cSSam Leffler 		return WPS_CONTINUE;
116439beb93cSSam Leffler 	}
116539beb93cSSam Leffler 
1166f05cddf9SRui Paulo 	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1167f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1168f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1169f05cddf9SRui Paulo 		return WPS_CONTINUE;
1170f05cddf9SRui Paulo 	}
1171f05cddf9SRui Paulo 
117239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
117339beb93cSSam Leffler 		   "attribute");
117439beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
117539beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
117639beb93cSSam Leffler 	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
117739beb93cSSam Leffler 		wpabuf_free(decrypted);
117839beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
117939beb93cSSam Leffler 		return WPS_CONTINUE;
118039beb93cSSam Leffler 	}
118139beb93cSSam Leffler 	wpabuf_free(decrypted);
118239beb93cSSam Leffler 
1183f05cddf9SRui Paulo 	if (wps->wps->ap)
1184f05cddf9SRui Paulo 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1185f05cddf9SRui Paulo 				   NULL);
1186f05cddf9SRui Paulo 
118739beb93cSSam Leffler 	wps->state = SEND_M7;
118839beb93cSSam Leffler 	return WPS_CONTINUE;
118939beb93cSSam Leffler }
119039beb93cSSam Leffler 
119139beb93cSSam Leffler 
119239beb93cSSam Leffler static enum wps_process_res wps_process_m8(struct wps_data *wps,
119339beb93cSSam Leffler 					   const struct wpabuf *msg,
119439beb93cSSam Leffler 					   struct wps_parse_attr *attr)
119539beb93cSSam Leffler {
119639beb93cSSam Leffler 	struct wpabuf *decrypted;
119739beb93cSSam Leffler 	struct wps_parse_attr eattr;
119839beb93cSSam Leffler 
119939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M8");
120039beb93cSSam Leffler 
120139beb93cSSam Leffler 	if (wps->state != RECV_M8) {
120239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
120339beb93cSSam Leffler 			   "receiving M8", wps->state);
120439beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
120539beb93cSSam Leffler 		return WPS_CONTINUE;
120639beb93cSSam Leffler 	}
120739beb93cSSam Leffler 
120839beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
120939beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg)) {
121039beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
121139beb93cSSam Leffler 		return WPS_CONTINUE;
121239beb93cSSam Leffler 	}
121339beb93cSSam Leffler 
1214f05cddf9SRui Paulo 	if (wps->wps->ap && wps->wps->ap_setup_locked) {
1215f05cddf9SRui Paulo 		/*
1216f05cddf9SRui Paulo 		 * Stop here if special ap_setup_locked == 2 mode allowed the
1217f05cddf9SRui Paulo 		 * protocol to continue beyond M2. This allows ER to learn the
1218f05cddf9SRui Paulo 		 * current AP settings without changing them.
1219f05cddf9SRui Paulo 		 */
1220f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1221f05cddf9SRui Paulo 			   "registration of a new Registrar");
1222f05cddf9SRui Paulo 		wps->config_error = WPS_CFG_SETUP_LOCKED;
1223f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1224f05cddf9SRui Paulo 		return WPS_CONTINUE;
1225f05cddf9SRui Paulo 	}
1226f05cddf9SRui Paulo 
122739beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
122839beb93cSSam Leffler 					      attr->encr_settings_len);
122939beb93cSSam Leffler 	if (decrypted == NULL) {
123039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
123139beb93cSSam Leffler 			   "Settings attribute");
123239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
123339beb93cSSam Leffler 		return WPS_CONTINUE;
123439beb93cSSam Leffler 	}
123539beb93cSSam Leffler 
1236f05cddf9SRui Paulo 	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1237f05cddf9SRui Paulo 				 attr->version2 != NULL) < 0) {
1238f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1239f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1240f05cddf9SRui Paulo 		return WPS_CONTINUE;
1241f05cddf9SRui Paulo 	}
1242f05cddf9SRui Paulo 
124339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
124439beb93cSSam Leffler 		   "attribute");
124539beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
124639beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
124739beb93cSSam Leffler 	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1248f05cddf9SRui Paulo 			      eattr.num_cred, attr->version2 != NULL) ||
1249f05cddf9SRui Paulo 	    wps_process_ap_settings_e(wps, &eattr, decrypted,
1250f05cddf9SRui Paulo 				      attr->version2 != NULL)) {
125139beb93cSSam Leffler 		wpabuf_free(decrypted);
125239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
125339beb93cSSam Leffler 		return WPS_CONTINUE;
125439beb93cSSam Leffler 	}
125539beb93cSSam Leffler 	wpabuf_free(decrypted);
125639beb93cSSam Leffler 
125739beb93cSSam Leffler 	wps->state = WPS_MSG_DONE;
125839beb93cSSam Leffler 	return WPS_CONTINUE;
125939beb93cSSam Leffler }
126039beb93cSSam Leffler 
126139beb93cSSam Leffler 
126239beb93cSSam Leffler static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
126339beb93cSSam Leffler 						const struct wpabuf *msg)
126439beb93cSSam Leffler {
126539beb93cSSam Leffler 	struct wps_parse_attr attr;
126639beb93cSSam Leffler 	enum wps_process_res ret = WPS_CONTINUE;
126739beb93cSSam Leffler 
126839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
126939beb93cSSam Leffler 
127039beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
127139beb93cSSam Leffler 		return WPS_FAILURE;
127239beb93cSSam Leffler 
127339beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1274f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
127539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
127639beb93cSSam Leffler 		return WPS_FAILURE;
127739beb93cSSam Leffler 	}
127839beb93cSSam Leffler 
127939beb93cSSam Leffler 	if (attr.msg_type == NULL) {
128039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1281f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1282f05cddf9SRui Paulo 		return WPS_CONTINUE;
128339beb93cSSam Leffler 	}
128439beb93cSSam Leffler 
128539beb93cSSam Leffler 	switch (*attr.msg_type) {
128639beb93cSSam Leffler 	case WPS_M2:
1287f05cddf9SRui Paulo 		if (wps_validate_m2(msg) < 0)
1288f05cddf9SRui Paulo 			return WPS_FAILURE;
128939beb93cSSam Leffler 		ret = wps_process_m2(wps, msg, &attr);
129039beb93cSSam Leffler 		break;
129139beb93cSSam Leffler 	case WPS_M2D:
1292f05cddf9SRui Paulo 		if (wps_validate_m2d(msg) < 0)
1293f05cddf9SRui Paulo 			return WPS_FAILURE;
129439beb93cSSam Leffler 		ret = wps_process_m2d(wps, &attr);
129539beb93cSSam Leffler 		break;
129639beb93cSSam Leffler 	case WPS_M4:
1297f05cddf9SRui Paulo 		if (wps_validate_m4(msg) < 0)
1298f05cddf9SRui Paulo 			return WPS_FAILURE;
129939beb93cSSam Leffler 		ret = wps_process_m4(wps, msg, &attr);
130039beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1301f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
1302*5b9c547cSRui Paulo 				       wps->error_indication,
1303*5b9c547cSRui Paulo 				       wps->peer_dev.mac_addr);
130439beb93cSSam Leffler 		break;
130539beb93cSSam Leffler 	case WPS_M6:
1306f05cddf9SRui Paulo 		if (wps_validate_m6(msg) < 0)
1307f05cddf9SRui Paulo 			return WPS_FAILURE;
130839beb93cSSam Leffler 		ret = wps_process_m6(wps, msg, &attr);
130939beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1310f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
1311*5b9c547cSRui Paulo 				       wps->error_indication,
1312*5b9c547cSRui Paulo 				       wps->peer_dev.mac_addr);
131339beb93cSSam Leffler 		break;
131439beb93cSSam Leffler 	case WPS_M8:
1315f05cddf9SRui Paulo 		if (wps_validate_m8(msg) < 0)
1316f05cddf9SRui Paulo 			return WPS_FAILURE;
131739beb93cSSam Leffler 		ret = wps_process_m8(wps, msg, &attr);
131839beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1319f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
1320*5b9c547cSRui Paulo 				       wps->error_indication,
1321*5b9c547cSRui Paulo 				       wps->peer_dev.mac_addr);
132239beb93cSSam Leffler 		break;
132339beb93cSSam Leffler 	default:
132439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
132539beb93cSSam Leffler 			   *attr.msg_type);
132639beb93cSSam Leffler 		return WPS_FAILURE;
132739beb93cSSam Leffler 	}
132839beb93cSSam Leffler 
132939beb93cSSam Leffler 	/*
133039beb93cSSam Leffler 	 * Save a copy of the last message for Authenticator derivation if we
133139beb93cSSam Leffler 	 * are continuing. However, skip M2D since it is not authenticated and
133239beb93cSSam Leffler 	 * neither is the ACK/NACK response frame. This allows the possibly
133339beb93cSSam Leffler 	 * following M2 to be processed correctly by using the previously sent
133439beb93cSSam Leffler 	 * M1 in Authenticator derivation.
133539beb93cSSam Leffler 	 */
133639beb93cSSam Leffler 	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
133739beb93cSSam Leffler 		/* Save a copy of the last message for Authenticator derivation
133839beb93cSSam Leffler 		 */
133939beb93cSSam Leffler 		wpabuf_free(wps->last_msg);
134039beb93cSSam Leffler 		wps->last_msg = wpabuf_dup(msg);
134139beb93cSSam Leffler 	}
134239beb93cSSam Leffler 
134339beb93cSSam Leffler 	return ret;
134439beb93cSSam Leffler }
134539beb93cSSam Leffler 
134639beb93cSSam Leffler 
134739beb93cSSam Leffler static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
134839beb93cSSam Leffler 						const struct wpabuf *msg)
134939beb93cSSam Leffler {
135039beb93cSSam Leffler 	struct wps_parse_attr attr;
135139beb93cSSam Leffler 
135239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
135339beb93cSSam Leffler 
135439beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
135539beb93cSSam Leffler 		return WPS_FAILURE;
135639beb93cSSam Leffler 
135739beb93cSSam Leffler 	if (attr.msg_type == NULL) {
135839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
135939beb93cSSam Leffler 		return WPS_FAILURE;
136039beb93cSSam Leffler 	}
136139beb93cSSam Leffler 
136239beb93cSSam Leffler 	if (*attr.msg_type != WPS_WSC_ACK) {
136339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
136439beb93cSSam Leffler 			   *attr.msg_type);
136539beb93cSSam Leffler 		return WPS_FAILURE;
136639beb93cSSam Leffler 	}
136739beb93cSSam Leffler 
136839beb93cSSam Leffler 	if (attr.registrar_nonce == NULL ||
1369f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
137039beb93cSSam Leffler 	{
137139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
137239beb93cSSam Leffler 		return WPS_FAILURE;
137339beb93cSSam Leffler 	}
137439beb93cSSam Leffler 
137539beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1376f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
137739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
137839beb93cSSam Leffler 		return WPS_FAILURE;
137939beb93cSSam Leffler 	}
138039beb93cSSam Leffler 
138139beb93cSSam Leffler 	if (wps->state == RECV_ACK && wps->wps->ap) {
138239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
138339beb93cSSam Leffler 			   "completed successfully");
1384*5b9c547cSRui Paulo 		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
138539beb93cSSam Leffler 		wps->state = WPS_FINISHED;
138639beb93cSSam Leffler 		return WPS_DONE;
138739beb93cSSam Leffler 	}
138839beb93cSSam Leffler 
138939beb93cSSam Leffler 	return WPS_FAILURE;
139039beb93cSSam Leffler }
139139beb93cSSam Leffler 
139239beb93cSSam Leffler 
139339beb93cSSam Leffler static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
139439beb93cSSam Leffler 						 const struct wpabuf *msg)
139539beb93cSSam Leffler {
139639beb93cSSam Leffler 	struct wps_parse_attr attr;
1397f05cddf9SRui Paulo 	u16 config_error;
139839beb93cSSam Leffler 
139939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
140039beb93cSSam Leffler 
140139beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
140239beb93cSSam Leffler 		return WPS_FAILURE;
140339beb93cSSam Leffler 
140439beb93cSSam Leffler 	if (attr.msg_type == NULL) {
140539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
140639beb93cSSam Leffler 		return WPS_FAILURE;
140739beb93cSSam Leffler 	}
140839beb93cSSam Leffler 
140939beb93cSSam Leffler 	if (*attr.msg_type != WPS_WSC_NACK) {
141039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
141139beb93cSSam Leffler 			   *attr.msg_type);
141239beb93cSSam Leffler 		return WPS_FAILURE;
141339beb93cSSam Leffler 	}
141439beb93cSSam Leffler 
141539beb93cSSam Leffler 	if (attr.registrar_nonce == NULL ||
1416f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
141739beb93cSSam Leffler 	{
141839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
141939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
142039beb93cSSam Leffler 			    attr.registrar_nonce, WPS_NONCE_LEN);
142139beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
142239beb93cSSam Leffler 			    wps->nonce_r, WPS_NONCE_LEN);
142339beb93cSSam Leffler 		return WPS_FAILURE;
142439beb93cSSam Leffler 	}
142539beb93cSSam Leffler 
142639beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1427f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
142839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
142939beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
143039beb93cSSam Leffler 			    attr.enrollee_nonce, WPS_NONCE_LEN);
143139beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
143239beb93cSSam Leffler 			    wps->nonce_e, WPS_NONCE_LEN);
143339beb93cSSam Leffler 		return WPS_FAILURE;
143439beb93cSSam Leffler 	}
143539beb93cSSam Leffler 
143639beb93cSSam Leffler 	if (attr.config_error == NULL) {
143739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
143839beb93cSSam Leffler 			   "in WSC_NACK");
143939beb93cSSam Leffler 		return WPS_FAILURE;
144039beb93cSSam Leffler 	}
144139beb93cSSam Leffler 
1442f05cddf9SRui Paulo 	config_error = WPA_GET_BE16(attr.config_error);
144339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1444f05cddf9SRui Paulo 		   "Configuration Error %d", config_error);
144539beb93cSSam Leffler 
144639beb93cSSam Leffler 	switch (wps->state) {
144739beb93cSSam Leffler 	case RECV_M4:
1448f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M3, config_error,
1449*5b9c547cSRui Paulo 			       wps->error_indication, wps->peer_dev.mac_addr);
145039beb93cSSam Leffler 		break;
145139beb93cSSam Leffler 	case RECV_M6:
1452f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M5, config_error,
1453*5b9c547cSRui Paulo 			       wps->error_indication, wps->peer_dev.mac_addr);
145439beb93cSSam Leffler 		break;
145539beb93cSSam Leffler 	case RECV_M8:
1456f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M7, config_error,
1457*5b9c547cSRui Paulo 			       wps->error_indication, wps->peer_dev.mac_addr);
145839beb93cSSam Leffler 		break;
145939beb93cSSam Leffler 	default:
146039beb93cSSam Leffler 		break;
146139beb93cSSam Leffler 	}
146239beb93cSSam Leffler 
146339beb93cSSam Leffler 	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
146439beb93cSSam Leffler 	 * Enrollee is Authenticator */
146539beb93cSSam Leffler 	wps->state = SEND_WSC_NACK;
146639beb93cSSam Leffler 
146739beb93cSSam Leffler 	return WPS_FAILURE;
146839beb93cSSam Leffler }
146939beb93cSSam Leffler 
147039beb93cSSam Leffler 
147139beb93cSSam Leffler enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
147239beb93cSSam Leffler 					      enum wsc_op_code op_code,
147339beb93cSSam Leffler 					      const struct wpabuf *msg)
147439beb93cSSam Leffler {
147539beb93cSSam Leffler 
147639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
147739beb93cSSam Leffler 		   "op_code=%d)",
147839beb93cSSam Leffler 		   (unsigned long) wpabuf_len(msg), op_code);
147939beb93cSSam Leffler 
14803157ba21SRui Paulo 	if (op_code == WSC_UPnP) {
14813157ba21SRui Paulo 		/* Determine the OpCode based on message type attribute */
14823157ba21SRui Paulo 		struct wps_parse_attr attr;
14833157ba21SRui Paulo 		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
14843157ba21SRui Paulo 			if (*attr.msg_type == WPS_WSC_ACK)
14853157ba21SRui Paulo 				op_code = WSC_ACK;
14863157ba21SRui Paulo 			else if (*attr.msg_type == WPS_WSC_NACK)
14873157ba21SRui Paulo 				op_code = WSC_NACK;
14883157ba21SRui Paulo 		}
14893157ba21SRui Paulo 	}
14903157ba21SRui Paulo 
149139beb93cSSam Leffler 	switch (op_code) {
149239beb93cSSam Leffler 	case WSC_MSG:
149339beb93cSSam Leffler 	case WSC_UPnP:
149439beb93cSSam Leffler 		return wps_process_wsc_msg(wps, msg);
149539beb93cSSam Leffler 	case WSC_ACK:
1496f05cddf9SRui Paulo 		if (wps_validate_wsc_ack(msg) < 0)
1497f05cddf9SRui Paulo 			return WPS_FAILURE;
149839beb93cSSam Leffler 		return wps_process_wsc_ack(wps, msg);
149939beb93cSSam Leffler 	case WSC_NACK:
1500f05cddf9SRui Paulo 		if (wps_validate_wsc_nack(msg) < 0)
1501f05cddf9SRui Paulo 			return WPS_FAILURE;
150239beb93cSSam Leffler 		return wps_process_wsc_nack(wps, msg);
150339beb93cSSam Leffler 	default:
150439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
150539beb93cSSam Leffler 		return WPS_FAILURE;
150639beb93cSSam Leffler 	}
150739beb93cSSam Leffler }
1508