xref: /freebsd/contrib/wpa/src/wps/wps_enrollee.c (revision 325151a32e114f02699a301c1e74080e7c1f1a26)
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) ||
1405b9c547cSRui 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) ||
1495b9c547cSRui Paulo 	    wps_build_rf_bands(&wps->wps->dev, msg,
1505b9c547cSRui 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 
1785b9c547cSRui Paulo 	if (wps->wps->ap && random_pool_ready() != 1) {
1795b9c547cSRui Paulo 		wpa_printf(MSG_INFO,
1805b9c547cSRui Paulo 			   "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
1815b9c547cSRui Paulo 		return NULL;
1825b9c547cSRui Paulo 	}
1835b9c547cSRui 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 {
2505b9c547cSRui Paulo 	u16 auth_type = wps->wps->ap_auth_type;
251f05cddf9SRui Paulo 
2525b9c547cSRui Paulo 	/*
2535b9c547cSRui Paulo 	 * Work around issues with Windows 7 WPS implementation not liking
2545b9c547cSRui Paulo 	 * multiple Authentication Type bits in M7 AP Settings attribute by
2555b9c547cSRui Paulo 	 * showing only the most secure option from current configuration.
2565b9c547cSRui 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 {
2745b9c547cSRui Paulo 	u16 encr_type = wps->wps->ap_encr_type;
275f05cddf9SRui Paulo 
2765b9c547cSRui Paulo 	/*
2775b9c547cSRui Paulo 	 * Work around issues with Windows 7 WPS implementation not liking
2785b9c547cSRui Paulo 	 * multiple Encryption Type bits in M7 AP Settings attribute by
2795b9c547cSRui Paulo 	 * showing only the most secure option from current configuration.
2805b9c547cSRui Paulo 	 */
2815b9c547cSRui 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 {
2985b9c547cSRui Paulo 	if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
2995b9c547cSRui Paulo 	    wps->wps->network_key_len == 0) {
3005b9c547cSRui Paulo 		char hex[65];
3015b9c547cSRui Paulo 		u8 psk[32];
3025b9c547cSRui Paulo 		/* Generate a random per-device PSK */
3035b9c547cSRui Paulo 		if (random_pool_ready() != 1 ||
3045b9c547cSRui Paulo 		    random_get_bytes(psk, sizeof(psk)) < 0) {
3055b9c547cSRui Paulo 			wpa_printf(MSG_INFO,
3065b9c547cSRui Paulo 				   "WPS: Could not generate random PSK");
3075b9c547cSRui Paulo 			return -1;
3085b9c547cSRui Paulo 		}
3095b9c547cSRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
3105b9c547cSRui Paulo 				psk, sizeof(psk));
3115b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
3125b9c547cSRui Paulo 			   (unsigned int) wps->new_psk_len * 2);
3135b9c547cSRui Paulo 		wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
3145b9c547cSRui Paulo 		wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
3155b9c547cSRui Paulo 		wpabuf_put_be16(msg, sizeof(psk) * 2);
3165b9c547cSRui Paulo 		wpabuf_put_data(msg, hex, sizeof(psk) * 2);
3175b9c547cSRui Paulo 		if (wps->wps->registrar) {
3185b9c547cSRui Paulo 			wps_cb_new_psk(wps->wps->registrar,
3195b9c547cSRui Paulo 				       wps->peer_dev.mac_addr,
3205b9c547cSRui Paulo 				       wps->p2p_dev_addr, psk, sizeof(psk));
3215b9c547cSRui Paulo 		}
3225b9c547cSRui Paulo 		return 0;
3235b9c547cSRui Paulo 	}
3245b9c547cSRui Paulo 
3255b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
3265b9c547cSRui 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 {
3465b9c547cSRui Paulo 	const u8 *start, *end;
3475b9c547cSRui Paulo 	int ret;
3485b9c547cSRui 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 
3565b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WPS:  * AP Settings based on current configuration");
3575b9c547cSRui Paulo 	start = wpabuf_put(plain, 0);
3585b9c547cSRui 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);
3635b9c547cSRui Paulo 	end = wpabuf_put(plain, 0);
3645b9c547cSRui Paulo 
3655b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
3665b9c547cSRui Paulo 			start, end - start);
3675b9c547cSRui Paulo 
3685b9c547cSRui 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 {
4405b9c547cSRui 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 
5615b9c547cSRui Paulo 	if (wps->peer_pubkey_hash_set) {
5625b9c547cSRui Paulo 		u8 hash[WPS_HASH_LEN];
5635b9c547cSRui Paulo 		sha256_vector(1, &pk, &pk_len, hash);
5645b9c547cSRui Paulo 		if (os_memcmp_const(hash, wps->peer_pubkey_hash,
5655b9c547cSRui Paulo 				    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
5665b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
5675b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
5685b9c547cSRui Paulo 				    pk, pk_len);
5695b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
5705b9c547cSRui Paulo 				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
5715b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
5725b9c547cSRui Paulo 				    wps->peer_pubkey_hash,
5735b9c547cSRui Paulo 				    WPS_OOB_PUBKEY_HASH_LEN);
5745b9c547cSRui Paulo 			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
5755b9c547cSRui Paulo 			return -1;
5765b9c547cSRui Paulo 		}
5775b9c547cSRui Paulo 	}
5785b9c547cSRui 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 
6445b9c547cSRui 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;
6485b9c547cSRui 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 
6845b9c547cSRui 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;
6885b9c547cSRui 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[],
762*325151a3SRui Paulo 			     u16 cred_len[], unsigned int 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;
802*325151a3SRui Paulo 	int ret = 0;
80339beb93cSSam Leffler 
80439beb93cSSam Leffler 	if (!wps->wps->ap)
80539beb93cSSam Leffler 		return 0;
80639beb93cSSam Leffler 
80739beb93cSSam Leffler 	if (wps_process_ap_settings(attr, &cred) < 0)
80839beb93cSSam Leffler 		return -1;
80939beb93cSSam Leffler 
81039beb93cSSam Leffler 	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
81139beb93cSSam Leffler 		   "Registrar");
81239beb93cSSam Leffler 
8133157ba21SRui Paulo 	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
8143157ba21SRui Paulo 	    0) {
8153157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
8163157ba21SRui Paulo 			   MACSTR ") does not match with own address (" MACSTR
8173157ba21SRui Paulo 			   ")", MAC2STR(cred.mac_addr),
8183157ba21SRui Paulo 			   MAC2STR(wps->wps->dev.mac_addr));
8193157ba21SRui Paulo 		/*
8203157ba21SRui Paulo 		 * In theory, this could be consider fatal error, but there are
8213157ba21SRui Paulo 		 * number of deployed implementations using other address here
8223157ba21SRui Paulo 		 * due to unclarity in the specification. For interoperability
8233157ba21SRui Paulo 		 * reasons, allow this to be processed since we do not really
8243157ba21SRui Paulo 		 * use the MAC Address information for anything.
8253157ba21SRui Paulo 		 */
826f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
827f05cddf9SRui Paulo 		if (wps2) {
828f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
829f05cddf9SRui Paulo 				   "MAC Address in AP Settings");
830f05cddf9SRui Paulo 			return -1;
8313157ba21SRui Paulo 		}
832f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
833f05cddf9SRui Paulo 	}
834f05cddf9SRui Paulo 
835f05cddf9SRui Paulo 	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
836f05cddf9SRui Paulo 	{
837f05cddf9SRui Paulo 		if (cred.encr_type & WPS_ENCR_WEP) {
838f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
839f05cddf9SRui Paulo 				   "due to WEP configuration");
840f05cddf9SRui Paulo 			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
841f05cddf9SRui Paulo 			return -1;
842f05cddf9SRui Paulo 		}
843f05cddf9SRui Paulo 
844f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
845f05cddf9SRui Paulo 			   "invalid encr_type 0x%x", cred.encr_type);
846f05cddf9SRui Paulo 		return -1;
847f05cddf9SRui Paulo 	}
848f05cddf9SRui Paulo 
849f05cddf9SRui Paulo #ifdef CONFIG_WPS_STRICT
850f05cddf9SRui Paulo 	if (wps2) {
851f05cddf9SRui Paulo 		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
852f05cddf9SRui Paulo 		    WPS_ENCR_TKIP ||
853f05cddf9SRui Paulo 		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
854f05cddf9SRui Paulo 		    WPS_AUTH_WPAPSK) {
855f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
856f05cddf9SRui Paulo 				   "AP Settings: WPA-Personal/TKIP only");
857f05cddf9SRui Paulo 			wps->error_indication =
858f05cddf9SRui Paulo 				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
859f05cddf9SRui Paulo 			return -1;
860f05cddf9SRui Paulo 		}
861f05cddf9SRui Paulo 	}
862f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
863f05cddf9SRui Paulo 
864f05cddf9SRui Paulo 	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
865f05cddf9SRui Paulo 	{
866f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
867f05cddf9SRui Paulo 			   "TKIP+AES");
868f05cddf9SRui Paulo 		cred.encr_type |= WPS_ENCR_AES;
869f05cddf9SRui Paulo 	}
870f05cddf9SRui Paulo 
871f05cddf9SRui Paulo 	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
872f05cddf9SRui Paulo 	    WPS_AUTH_WPAPSK) {
873f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
874f05cddf9SRui Paulo 			   "WPAPSK+WPA2PSK");
875f05cddf9SRui Paulo 		cred.auth_type |= WPS_AUTH_WPA2PSK;
876f05cddf9SRui Paulo 	}
8773157ba21SRui Paulo 
87839beb93cSSam Leffler 	if (wps->wps->cred_cb) {
87939beb93cSSam Leffler 		cred.cred_attr = wpabuf_head(attrs);
88039beb93cSSam Leffler 		cred.cred_attr_len = wpabuf_len(attrs);
881*325151a3SRui Paulo 		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
88239beb93cSSam Leffler 	}
88339beb93cSSam Leffler 
884*325151a3SRui Paulo 	return ret;
88539beb93cSSam Leffler }
88639beb93cSSam Leffler 
88739beb93cSSam Leffler 
8885b9c547cSRui Paulo static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
8895b9c547cSRui Paulo {
8905b9c547cSRui Paulo 	u16 id;
8915b9c547cSRui Paulo 
8925b9c547cSRui Paulo 	if (dev_pw_id == NULL) {
8935b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
8945b9c547cSRui Paulo 		return -1;
8955b9c547cSRui Paulo 	}
8965b9c547cSRui Paulo 
8975b9c547cSRui Paulo 	id = WPA_GET_BE16(dev_pw_id);
8985b9c547cSRui Paulo 	if (wps->dev_pw_id == id) {
8995b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
9005b9c547cSRui Paulo 		return 0;
9015b9c547cSRui Paulo 	}
9025b9c547cSRui Paulo 
9035b9c547cSRui Paulo #ifdef CONFIG_P2P
9045b9c547cSRui Paulo 	if ((id == DEV_PW_DEFAULT &&
9055b9c547cSRui Paulo 	     wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
9065b9c547cSRui Paulo 	    (id == DEV_PW_REGISTRAR_SPECIFIED &&
9075b9c547cSRui Paulo 	     wps->dev_pw_id == DEV_PW_DEFAULT)) {
9085b9c547cSRui Paulo 		/*
9095b9c547cSRui Paulo 		 * Common P2P use cases indicate whether the PIN is from the
9105b9c547cSRui Paulo 		 * client or GO using Device Password Id in M1/M2 in a way that
9115b9c547cSRui Paulo 		 * does not look fully compliant with WSC specification. Anyway,
9125b9c547cSRui Paulo 		 * this is deployed and needs to be allowed, so ignore changes
9135b9c547cSRui Paulo 		 * between Registrar-Specified and Default PIN.
9145b9c547cSRui Paulo 		 */
9155b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
9165b9c547cSRui Paulo 			   "change");
9175b9c547cSRui Paulo 		return 0;
9185b9c547cSRui Paulo 	}
9195b9c547cSRui Paulo #endif /* CONFIG_P2P */
9205b9c547cSRui Paulo 
9215b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
9225b9c547cSRui Paulo 		   "ID from %u to %u", wps->dev_pw_id, id);
9235b9c547cSRui Paulo 
9245b9c547cSRui Paulo 	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
9255b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG,
9265b9c547cSRui Paulo 			   "WPS: Workaround - ignore PBC-to-PIN change");
9275b9c547cSRui Paulo 		return 0;
9285b9c547cSRui Paulo 	}
9295b9c547cSRui Paulo 
9305b9c547cSRui Paulo 	if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
9315b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
9325b9c547cSRui Paulo 		bin_clear_free(wps->dev_password, wps->dev_password_len);
9335b9c547cSRui Paulo 		wps->dev_pw_id = wps->alt_dev_pw_id;
9345b9c547cSRui Paulo 		wps->dev_password = wps->alt_dev_password;
9355b9c547cSRui Paulo 		wps->dev_password_len = wps->alt_dev_password_len;
9365b9c547cSRui Paulo 		wps->alt_dev_password = NULL;
9375b9c547cSRui Paulo 		wps->alt_dev_password_len = 0;
9385b9c547cSRui Paulo 		return 0;
9395b9c547cSRui Paulo 	}
9405b9c547cSRui Paulo 
9415b9c547cSRui Paulo 	return -1;
9425b9c547cSRui Paulo }
9435b9c547cSRui Paulo 
9445b9c547cSRui Paulo 
94539beb93cSSam Leffler static enum wps_process_res wps_process_m2(struct wps_data *wps,
94639beb93cSSam Leffler 					   const struct wpabuf *msg,
94739beb93cSSam Leffler 					   struct wps_parse_attr *attr)
94839beb93cSSam Leffler {
94939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M2");
95039beb93cSSam Leffler 
95139beb93cSSam Leffler 	if (wps->state != RECV_M2) {
95239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
95339beb93cSSam Leffler 			   "receiving M2", wps->state);
95439beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
95539beb93cSSam Leffler 		return WPS_CONTINUE;
95639beb93cSSam Leffler 	}
95739beb93cSSam Leffler 
95839beb93cSSam Leffler 	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
95939beb93cSSam Leffler 	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
9605b9c547cSRui Paulo 	    wps_process_uuid_r(wps, attr->uuid_r) ||
9615b9c547cSRui Paulo 	    wps_process_dev_pw_id(wps, attr->dev_password_id)) {
96239beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
96339beb93cSSam Leffler 		return WPS_CONTINUE;
96439beb93cSSam Leffler 	}
96539beb93cSSam Leffler 
966f05cddf9SRui Paulo 	/*
967f05cddf9SRui Paulo 	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
968f05cddf9SRui Paulo 	 * special locked mode is used to allow protocol run up to M7 in order
969f05cddf9SRui Paulo 	 * to support external Registrars that only learn the current AP
970f05cddf9SRui Paulo 	 * configuration without changing it.
971f05cddf9SRui Paulo 	 */
972e28a4053SRui Paulo 	if (wps->wps->ap &&
973f05cddf9SRui Paulo 	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
974f05cddf9SRui Paulo 	     wps->dev_password == NULL)) {
97539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
97639beb93cSSam Leffler 			   "registration of a new Registrar");
97739beb93cSSam Leffler 		wps->config_error = WPS_CFG_SETUP_LOCKED;
97839beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
97939beb93cSSam Leffler 		return WPS_CONTINUE;
98039beb93cSSam Leffler 	}
98139beb93cSSam Leffler 
982e28a4053SRui Paulo 	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
983e28a4053SRui Paulo 	    wps_process_authenticator(wps, attr->authenticator, msg) ||
984e28a4053SRui Paulo 	    wps_process_device_attrs(&wps->peer_dev, attr)) {
985e28a4053SRui Paulo 		wps->state = SEND_WSC_NACK;
986e28a4053SRui Paulo 		return WPS_CONTINUE;
987e28a4053SRui Paulo 	}
988e28a4053SRui Paulo 
9895b9c547cSRui Paulo #ifdef CONFIG_WPS_NFC
9905b9c547cSRui Paulo 	if (wps->peer_pubkey_hash_set) {
9915b9c547cSRui Paulo 		struct wpabuf *decrypted;
9925b9c547cSRui Paulo 		struct wps_parse_attr eattr;
9935b9c547cSRui Paulo 
9945b9c547cSRui Paulo 		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
9955b9c547cSRui Paulo 						      attr->encr_settings_len);
9965b9c547cSRui Paulo 		if (decrypted == NULL) {
9975b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
9985b9c547cSRui Paulo 				   "Encrypted Settings attribute");
9995b9c547cSRui Paulo 			wps->state = SEND_WSC_NACK;
10005b9c547cSRui Paulo 			return WPS_CONTINUE;
10015b9c547cSRui Paulo 		}
10025b9c547cSRui Paulo 
10035b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
10045b9c547cSRui Paulo 			   "Settings attribute");
10055b9c547cSRui Paulo 		if (wps_parse_msg(decrypted, &eattr) < 0 ||
10065b9c547cSRui Paulo 		    wps_process_key_wrap_auth(wps, decrypted,
10075b9c547cSRui Paulo 					      eattr.key_wrap_auth) ||
10085b9c547cSRui Paulo 		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
10095b9c547cSRui Paulo 				      eattr.num_cred, attr->version2 != NULL)) {
10105b9c547cSRui Paulo 			wpabuf_free(decrypted);
10115b9c547cSRui Paulo 			wps->state = SEND_WSC_NACK;
10125b9c547cSRui Paulo 			return WPS_CONTINUE;
10135b9c547cSRui Paulo 		}
10145b9c547cSRui Paulo 		wpabuf_free(decrypted);
10155b9c547cSRui Paulo 
10165b9c547cSRui Paulo 		wps->state = WPS_MSG_DONE;
10175b9c547cSRui Paulo 		return WPS_CONTINUE;
10185b9c547cSRui Paulo 	}
10195b9c547cSRui Paulo #endif /* CONFIG_WPS_NFC */
10205b9c547cSRui Paulo 
102139beb93cSSam Leffler 	wps->state = SEND_M3;
102239beb93cSSam Leffler 	return WPS_CONTINUE;
102339beb93cSSam Leffler }
102439beb93cSSam Leffler 
102539beb93cSSam Leffler 
102639beb93cSSam Leffler static enum wps_process_res wps_process_m2d(struct wps_data *wps,
102739beb93cSSam Leffler 					    struct wps_parse_attr *attr)
102839beb93cSSam Leffler {
102939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
103039beb93cSSam Leffler 
103139beb93cSSam Leffler 	if (wps->state != RECV_M2) {
103239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
103339beb93cSSam Leffler 			   "receiving M2D", wps->state);
103439beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
103539beb93cSSam Leffler 		return WPS_CONTINUE;
103639beb93cSSam Leffler 	}
103739beb93cSSam Leffler 
103839beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
103939beb93cSSam Leffler 			  attr->manufacturer, attr->manufacturer_len);
104039beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
104139beb93cSSam Leffler 			  attr->model_name, attr->model_name_len);
104239beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
104339beb93cSSam Leffler 			  attr->model_number, attr->model_number_len);
104439beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
104539beb93cSSam Leffler 			  attr->serial_number, attr->serial_number_len);
104639beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
104739beb93cSSam Leffler 			  attr->dev_name, attr->dev_name_len);
104839beb93cSSam Leffler 
104939beb93cSSam Leffler 	if (wps->wps->event_cb) {
105039beb93cSSam Leffler 		union wps_event_data data;
105139beb93cSSam Leffler 		struct wps_event_m2d *m2d = &data.m2d;
105239beb93cSSam Leffler 		os_memset(&data, 0, sizeof(data));
105339beb93cSSam Leffler 		if (attr->config_methods)
105439beb93cSSam Leffler 			m2d->config_methods =
105539beb93cSSam Leffler 				WPA_GET_BE16(attr->config_methods);
105639beb93cSSam Leffler 		m2d->manufacturer = attr->manufacturer;
105739beb93cSSam Leffler 		m2d->manufacturer_len = attr->manufacturer_len;
105839beb93cSSam Leffler 		m2d->model_name = attr->model_name;
105939beb93cSSam Leffler 		m2d->model_name_len = attr->model_name_len;
106039beb93cSSam Leffler 		m2d->model_number = attr->model_number;
106139beb93cSSam Leffler 		m2d->model_number_len = attr->model_number_len;
106239beb93cSSam Leffler 		m2d->serial_number = attr->serial_number;
106339beb93cSSam Leffler 		m2d->serial_number_len = attr->serial_number_len;
106439beb93cSSam Leffler 		m2d->dev_name = attr->dev_name;
106539beb93cSSam Leffler 		m2d->dev_name_len = attr->dev_name_len;
106639beb93cSSam Leffler 		m2d->primary_dev_type = attr->primary_dev_type;
106739beb93cSSam Leffler 		if (attr->config_error)
106839beb93cSSam Leffler 			m2d->config_error =
106939beb93cSSam Leffler 				WPA_GET_BE16(attr->config_error);
107039beb93cSSam Leffler 		if (attr->dev_password_id)
107139beb93cSSam Leffler 			m2d->dev_password_id =
107239beb93cSSam Leffler 				WPA_GET_BE16(attr->dev_password_id);
107339beb93cSSam Leffler 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
107439beb93cSSam Leffler 	}
107539beb93cSSam Leffler 
107639beb93cSSam Leffler 	wps->state = RECEIVED_M2D;
107739beb93cSSam Leffler 	return WPS_CONTINUE;
107839beb93cSSam Leffler }
107939beb93cSSam Leffler 
108039beb93cSSam Leffler 
108139beb93cSSam Leffler static enum wps_process_res wps_process_m4(struct wps_data *wps,
108239beb93cSSam Leffler 					   const struct wpabuf *msg,
108339beb93cSSam Leffler 					   struct wps_parse_attr *attr)
108439beb93cSSam Leffler {
108539beb93cSSam Leffler 	struct wpabuf *decrypted;
108639beb93cSSam Leffler 	struct wps_parse_attr eattr;
108739beb93cSSam Leffler 
108839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M4");
108939beb93cSSam Leffler 
109039beb93cSSam Leffler 	if (wps->state != RECV_M4) {
109139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
109239beb93cSSam Leffler 			   "receiving M4", wps->state);
109339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
109439beb93cSSam Leffler 		return WPS_CONTINUE;
109539beb93cSSam Leffler 	}
109639beb93cSSam Leffler 
109739beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
109839beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg) ||
109939beb93cSSam Leffler 	    wps_process_r_hash1(wps, attr->r_hash1) ||
110039beb93cSSam Leffler 	    wps_process_r_hash2(wps, attr->r_hash2)) {
110139beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
110239beb93cSSam Leffler 		return WPS_CONTINUE;
110339beb93cSSam Leffler 	}
110439beb93cSSam Leffler 
110539beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
110639beb93cSSam Leffler 					      attr->encr_settings_len);
110739beb93cSSam Leffler 	if (decrypted == NULL) {
110839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
110939beb93cSSam Leffler 			   "Settings attribute");
111039beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
111139beb93cSSam Leffler 		return WPS_CONTINUE;
111239beb93cSSam Leffler 	}
111339beb93cSSam Leffler 
1114f05cddf9SRui Paulo 	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
1115f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1116f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1117f05cddf9SRui Paulo 		return WPS_CONTINUE;
1118f05cddf9SRui Paulo 	}
1119f05cddf9SRui Paulo 
112039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
112139beb93cSSam Leffler 		   "attribute");
112239beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
112339beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
112439beb93cSSam Leffler 	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
112539beb93cSSam Leffler 		wpabuf_free(decrypted);
112639beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
112739beb93cSSam Leffler 		return WPS_CONTINUE;
112839beb93cSSam Leffler 	}
112939beb93cSSam Leffler 	wpabuf_free(decrypted);
113039beb93cSSam Leffler 
113139beb93cSSam Leffler 	wps->state = SEND_M5;
113239beb93cSSam Leffler 	return WPS_CONTINUE;
113339beb93cSSam Leffler }
113439beb93cSSam Leffler 
113539beb93cSSam Leffler 
113639beb93cSSam Leffler static enum wps_process_res wps_process_m6(struct wps_data *wps,
113739beb93cSSam Leffler 					   const struct wpabuf *msg,
113839beb93cSSam Leffler 					   struct wps_parse_attr *attr)
113939beb93cSSam Leffler {
114039beb93cSSam Leffler 	struct wpabuf *decrypted;
114139beb93cSSam Leffler 	struct wps_parse_attr eattr;
114239beb93cSSam Leffler 
114339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M6");
114439beb93cSSam Leffler 
114539beb93cSSam Leffler 	if (wps->state != RECV_M6) {
114639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
114739beb93cSSam Leffler 			   "receiving M6", wps->state);
114839beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
114939beb93cSSam Leffler 		return WPS_CONTINUE;
115039beb93cSSam Leffler 	}
115139beb93cSSam Leffler 
115239beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
115339beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg)) {
115439beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
115539beb93cSSam Leffler 		return WPS_CONTINUE;
115639beb93cSSam Leffler 	}
115739beb93cSSam Leffler 
115839beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
115939beb93cSSam Leffler 					      attr->encr_settings_len);
116039beb93cSSam Leffler 	if (decrypted == NULL) {
116139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
116239beb93cSSam Leffler 			   "Settings attribute");
116339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
116439beb93cSSam Leffler 		return WPS_CONTINUE;
116539beb93cSSam Leffler 	}
116639beb93cSSam Leffler 
1167f05cddf9SRui Paulo 	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1168f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1169f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1170f05cddf9SRui Paulo 		return WPS_CONTINUE;
1171f05cddf9SRui Paulo 	}
1172f05cddf9SRui Paulo 
117339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
117439beb93cSSam Leffler 		   "attribute");
117539beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
117639beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
117739beb93cSSam Leffler 	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
117839beb93cSSam Leffler 		wpabuf_free(decrypted);
117939beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
118039beb93cSSam Leffler 		return WPS_CONTINUE;
118139beb93cSSam Leffler 	}
118239beb93cSSam Leffler 	wpabuf_free(decrypted);
118339beb93cSSam Leffler 
1184f05cddf9SRui Paulo 	if (wps->wps->ap)
1185f05cddf9SRui Paulo 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1186f05cddf9SRui Paulo 				   NULL);
1187f05cddf9SRui Paulo 
118839beb93cSSam Leffler 	wps->state = SEND_M7;
118939beb93cSSam Leffler 	return WPS_CONTINUE;
119039beb93cSSam Leffler }
119139beb93cSSam Leffler 
119239beb93cSSam Leffler 
119339beb93cSSam Leffler static enum wps_process_res wps_process_m8(struct wps_data *wps,
119439beb93cSSam Leffler 					   const struct wpabuf *msg,
119539beb93cSSam Leffler 					   struct wps_parse_attr *attr)
119639beb93cSSam Leffler {
119739beb93cSSam Leffler 	struct wpabuf *decrypted;
119839beb93cSSam Leffler 	struct wps_parse_attr eattr;
119939beb93cSSam Leffler 
120039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received M8");
120139beb93cSSam Leffler 
120239beb93cSSam Leffler 	if (wps->state != RECV_M8) {
120339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
120439beb93cSSam Leffler 			   "receiving M8", wps->state);
120539beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
120639beb93cSSam Leffler 		return WPS_CONTINUE;
120739beb93cSSam Leffler 	}
120839beb93cSSam Leffler 
120939beb93cSSam Leffler 	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
121039beb93cSSam Leffler 	    wps_process_authenticator(wps, attr->authenticator, msg)) {
121139beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
121239beb93cSSam Leffler 		return WPS_CONTINUE;
121339beb93cSSam Leffler 	}
121439beb93cSSam Leffler 
1215f05cddf9SRui Paulo 	if (wps->wps->ap && wps->wps->ap_setup_locked) {
1216f05cddf9SRui Paulo 		/*
1217f05cddf9SRui Paulo 		 * Stop here if special ap_setup_locked == 2 mode allowed the
1218f05cddf9SRui Paulo 		 * protocol to continue beyond M2. This allows ER to learn the
1219f05cddf9SRui Paulo 		 * current AP settings without changing them.
1220f05cddf9SRui Paulo 		 */
1221f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1222f05cddf9SRui Paulo 			   "registration of a new Registrar");
1223f05cddf9SRui Paulo 		wps->config_error = WPS_CFG_SETUP_LOCKED;
1224f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1225f05cddf9SRui Paulo 		return WPS_CONTINUE;
1226f05cddf9SRui Paulo 	}
1227f05cddf9SRui Paulo 
122839beb93cSSam Leffler 	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
122939beb93cSSam Leffler 					      attr->encr_settings_len);
123039beb93cSSam Leffler 	if (decrypted == NULL) {
123139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
123239beb93cSSam Leffler 			   "Settings attribute");
123339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
123439beb93cSSam Leffler 		return WPS_CONTINUE;
123539beb93cSSam Leffler 	}
123639beb93cSSam Leffler 
1237f05cddf9SRui Paulo 	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1238f05cddf9SRui Paulo 				 attr->version2 != NULL) < 0) {
1239f05cddf9SRui Paulo 		wpabuf_free(decrypted);
1240f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1241f05cddf9SRui Paulo 		return WPS_CONTINUE;
1242f05cddf9SRui Paulo 	}
1243f05cddf9SRui Paulo 
124439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
124539beb93cSSam Leffler 		   "attribute");
124639beb93cSSam Leffler 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
124739beb93cSSam Leffler 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
124839beb93cSSam Leffler 	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1249f05cddf9SRui Paulo 			      eattr.num_cred, attr->version2 != NULL) ||
1250f05cddf9SRui Paulo 	    wps_process_ap_settings_e(wps, &eattr, decrypted,
1251f05cddf9SRui Paulo 				      attr->version2 != NULL)) {
125239beb93cSSam Leffler 		wpabuf_free(decrypted);
125339beb93cSSam Leffler 		wps->state = SEND_WSC_NACK;
125439beb93cSSam Leffler 		return WPS_CONTINUE;
125539beb93cSSam Leffler 	}
125639beb93cSSam Leffler 	wpabuf_free(decrypted);
125739beb93cSSam Leffler 
125839beb93cSSam Leffler 	wps->state = WPS_MSG_DONE;
125939beb93cSSam Leffler 	return WPS_CONTINUE;
126039beb93cSSam Leffler }
126139beb93cSSam Leffler 
126239beb93cSSam Leffler 
126339beb93cSSam Leffler static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
126439beb93cSSam Leffler 						const struct wpabuf *msg)
126539beb93cSSam Leffler {
126639beb93cSSam Leffler 	struct wps_parse_attr attr;
126739beb93cSSam Leffler 	enum wps_process_res ret = WPS_CONTINUE;
126839beb93cSSam Leffler 
126939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
127039beb93cSSam Leffler 
127139beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
127239beb93cSSam Leffler 		return WPS_FAILURE;
127339beb93cSSam Leffler 
127439beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1275f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
127639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
127739beb93cSSam Leffler 		return WPS_FAILURE;
127839beb93cSSam Leffler 	}
127939beb93cSSam Leffler 
128039beb93cSSam Leffler 	if (attr.msg_type == NULL) {
128139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1282f05cddf9SRui Paulo 		wps->state = SEND_WSC_NACK;
1283f05cddf9SRui Paulo 		return WPS_CONTINUE;
128439beb93cSSam Leffler 	}
128539beb93cSSam Leffler 
128639beb93cSSam Leffler 	switch (*attr.msg_type) {
128739beb93cSSam Leffler 	case WPS_M2:
1288f05cddf9SRui Paulo 		if (wps_validate_m2(msg) < 0)
1289f05cddf9SRui Paulo 			return WPS_FAILURE;
129039beb93cSSam Leffler 		ret = wps_process_m2(wps, msg, &attr);
129139beb93cSSam Leffler 		break;
129239beb93cSSam Leffler 	case WPS_M2D:
1293f05cddf9SRui Paulo 		if (wps_validate_m2d(msg) < 0)
1294f05cddf9SRui Paulo 			return WPS_FAILURE;
129539beb93cSSam Leffler 		ret = wps_process_m2d(wps, &attr);
129639beb93cSSam Leffler 		break;
129739beb93cSSam Leffler 	case WPS_M4:
1298f05cddf9SRui Paulo 		if (wps_validate_m4(msg) < 0)
1299f05cddf9SRui Paulo 			return WPS_FAILURE;
130039beb93cSSam Leffler 		ret = wps_process_m4(wps, msg, &attr);
130139beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1302f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
13035b9c547cSRui Paulo 				       wps->error_indication,
13045b9c547cSRui Paulo 				       wps->peer_dev.mac_addr);
130539beb93cSSam Leffler 		break;
130639beb93cSSam Leffler 	case WPS_M6:
1307f05cddf9SRui Paulo 		if (wps_validate_m6(msg) < 0)
1308f05cddf9SRui Paulo 			return WPS_FAILURE;
130939beb93cSSam Leffler 		ret = wps_process_m6(wps, msg, &attr);
131039beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1311f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
13125b9c547cSRui Paulo 				       wps->error_indication,
13135b9c547cSRui Paulo 				       wps->peer_dev.mac_addr);
131439beb93cSSam Leffler 		break;
131539beb93cSSam Leffler 	case WPS_M8:
1316f05cddf9SRui Paulo 		if (wps_validate_m8(msg) < 0)
1317f05cddf9SRui Paulo 			return WPS_FAILURE;
131839beb93cSSam Leffler 		ret = wps_process_m8(wps, msg, &attr);
131939beb93cSSam Leffler 		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1320f05cddf9SRui Paulo 			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
13215b9c547cSRui Paulo 				       wps->error_indication,
13225b9c547cSRui Paulo 				       wps->peer_dev.mac_addr);
132339beb93cSSam Leffler 		break;
132439beb93cSSam Leffler 	default:
132539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
132639beb93cSSam Leffler 			   *attr.msg_type);
132739beb93cSSam Leffler 		return WPS_FAILURE;
132839beb93cSSam Leffler 	}
132939beb93cSSam Leffler 
133039beb93cSSam Leffler 	/*
133139beb93cSSam Leffler 	 * Save a copy of the last message for Authenticator derivation if we
133239beb93cSSam Leffler 	 * are continuing. However, skip M2D since it is not authenticated and
133339beb93cSSam Leffler 	 * neither is the ACK/NACK response frame. This allows the possibly
133439beb93cSSam Leffler 	 * following M2 to be processed correctly by using the previously sent
133539beb93cSSam Leffler 	 * M1 in Authenticator derivation.
133639beb93cSSam Leffler 	 */
133739beb93cSSam Leffler 	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
133839beb93cSSam Leffler 		/* Save a copy of the last message for Authenticator derivation
133939beb93cSSam Leffler 		 */
134039beb93cSSam Leffler 		wpabuf_free(wps->last_msg);
134139beb93cSSam Leffler 		wps->last_msg = wpabuf_dup(msg);
134239beb93cSSam Leffler 	}
134339beb93cSSam Leffler 
134439beb93cSSam Leffler 	return ret;
134539beb93cSSam Leffler }
134639beb93cSSam Leffler 
134739beb93cSSam Leffler 
134839beb93cSSam Leffler static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
134939beb93cSSam Leffler 						const struct wpabuf *msg)
135039beb93cSSam Leffler {
135139beb93cSSam Leffler 	struct wps_parse_attr attr;
135239beb93cSSam Leffler 
135339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
135439beb93cSSam Leffler 
135539beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
135639beb93cSSam Leffler 		return WPS_FAILURE;
135739beb93cSSam Leffler 
135839beb93cSSam Leffler 	if (attr.msg_type == NULL) {
135939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
136039beb93cSSam Leffler 		return WPS_FAILURE;
136139beb93cSSam Leffler 	}
136239beb93cSSam Leffler 
136339beb93cSSam Leffler 	if (*attr.msg_type != WPS_WSC_ACK) {
136439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
136539beb93cSSam Leffler 			   *attr.msg_type);
136639beb93cSSam Leffler 		return WPS_FAILURE;
136739beb93cSSam Leffler 	}
136839beb93cSSam Leffler 
136939beb93cSSam Leffler 	if (attr.registrar_nonce == NULL ||
1370f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
137139beb93cSSam Leffler 	{
137239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
137339beb93cSSam Leffler 		return WPS_FAILURE;
137439beb93cSSam Leffler 	}
137539beb93cSSam Leffler 
137639beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1377f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
137839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
137939beb93cSSam Leffler 		return WPS_FAILURE;
138039beb93cSSam Leffler 	}
138139beb93cSSam Leffler 
138239beb93cSSam Leffler 	if (wps->state == RECV_ACK && wps->wps->ap) {
138339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
138439beb93cSSam Leffler 			   "completed successfully");
13855b9c547cSRui Paulo 		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
138639beb93cSSam Leffler 		wps->state = WPS_FINISHED;
138739beb93cSSam Leffler 		return WPS_DONE;
138839beb93cSSam Leffler 	}
138939beb93cSSam Leffler 
139039beb93cSSam Leffler 	return WPS_FAILURE;
139139beb93cSSam Leffler }
139239beb93cSSam Leffler 
139339beb93cSSam Leffler 
139439beb93cSSam Leffler static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
139539beb93cSSam Leffler 						 const struct wpabuf *msg)
139639beb93cSSam Leffler {
139739beb93cSSam Leffler 	struct wps_parse_attr attr;
1398f05cddf9SRui Paulo 	u16 config_error;
139939beb93cSSam Leffler 
140039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
140139beb93cSSam Leffler 
140239beb93cSSam Leffler 	if (wps_parse_msg(msg, &attr) < 0)
140339beb93cSSam Leffler 		return WPS_FAILURE;
140439beb93cSSam Leffler 
140539beb93cSSam Leffler 	if (attr.msg_type == NULL) {
140639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
140739beb93cSSam Leffler 		return WPS_FAILURE;
140839beb93cSSam Leffler 	}
140939beb93cSSam Leffler 
141039beb93cSSam Leffler 	if (*attr.msg_type != WPS_WSC_NACK) {
141139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
141239beb93cSSam Leffler 			   *attr.msg_type);
141339beb93cSSam Leffler 		return WPS_FAILURE;
141439beb93cSSam Leffler 	}
141539beb93cSSam Leffler 
141639beb93cSSam Leffler 	if (attr.registrar_nonce == NULL ||
1417f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
141839beb93cSSam Leffler 	{
141939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
142039beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
142139beb93cSSam Leffler 			    attr.registrar_nonce, WPS_NONCE_LEN);
142239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
142339beb93cSSam Leffler 			    wps->nonce_r, WPS_NONCE_LEN);
142439beb93cSSam Leffler 		return WPS_FAILURE;
142539beb93cSSam Leffler 	}
142639beb93cSSam Leffler 
142739beb93cSSam Leffler 	if (attr.enrollee_nonce == NULL ||
1428f05cddf9SRui Paulo 	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
142939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
143039beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
143139beb93cSSam Leffler 			    attr.enrollee_nonce, WPS_NONCE_LEN);
143239beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
143339beb93cSSam Leffler 			    wps->nonce_e, WPS_NONCE_LEN);
143439beb93cSSam Leffler 		return WPS_FAILURE;
143539beb93cSSam Leffler 	}
143639beb93cSSam Leffler 
143739beb93cSSam Leffler 	if (attr.config_error == NULL) {
143839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
143939beb93cSSam Leffler 			   "in WSC_NACK");
144039beb93cSSam Leffler 		return WPS_FAILURE;
144139beb93cSSam Leffler 	}
144239beb93cSSam Leffler 
1443f05cddf9SRui Paulo 	config_error = WPA_GET_BE16(attr.config_error);
144439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1445f05cddf9SRui Paulo 		   "Configuration Error %d", config_error);
144639beb93cSSam Leffler 
144739beb93cSSam Leffler 	switch (wps->state) {
144839beb93cSSam Leffler 	case RECV_M4:
1449f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M3, config_error,
14505b9c547cSRui Paulo 			       wps->error_indication, wps->peer_dev.mac_addr);
145139beb93cSSam Leffler 		break;
145239beb93cSSam Leffler 	case RECV_M6:
1453f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M5, config_error,
14545b9c547cSRui Paulo 			       wps->error_indication, wps->peer_dev.mac_addr);
145539beb93cSSam Leffler 		break;
145639beb93cSSam Leffler 	case RECV_M8:
1457f05cddf9SRui Paulo 		wps_fail_event(wps->wps, WPS_M7, config_error,
14585b9c547cSRui Paulo 			       wps->error_indication, wps->peer_dev.mac_addr);
145939beb93cSSam Leffler 		break;
146039beb93cSSam Leffler 	default:
146139beb93cSSam Leffler 		break;
146239beb93cSSam Leffler 	}
146339beb93cSSam Leffler 
146439beb93cSSam Leffler 	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
146539beb93cSSam Leffler 	 * Enrollee is Authenticator */
146639beb93cSSam Leffler 	wps->state = SEND_WSC_NACK;
146739beb93cSSam Leffler 
146839beb93cSSam Leffler 	return WPS_FAILURE;
146939beb93cSSam Leffler }
147039beb93cSSam Leffler 
147139beb93cSSam Leffler 
147239beb93cSSam Leffler enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
147339beb93cSSam Leffler 					      enum wsc_op_code op_code,
147439beb93cSSam Leffler 					      const struct wpabuf *msg)
147539beb93cSSam Leffler {
147639beb93cSSam Leffler 
147739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
147839beb93cSSam Leffler 		   "op_code=%d)",
147939beb93cSSam Leffler 		   (unsigned long) wpabuf_len(msg), op_code);
148039beb93cSSam Leffler 
14813157ba21SRui Paulo 	if (op_code == WSC_UPnP) {
14823157ba21SRui Paulo 		/* Determine the OpCode based on message type attribute */
14833157ba21SRui Paulo 		struct wps_parse_attr attr;
14843157ba21SRui Paulo 		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
14853157ba21SRui Paulo 			if (*attr.msg_type == WPS_WSC_ACK)
14863157ba21SRui Paulo 				op_code = WSC_ACK;
14873157ba21SRui Paulo 			else if (*attr.msg_type == WPS_WSC_NACK)
14883157ba21SRui Paulo 				op_code = WSC_NACK;
14893157ba21SRui Paulo 		}
14903157ba21SRui Paulo 	}
14913157ba21SRui Paulo 
149239beb93cSSam Leffler 	switch (op_code) {
149339beb93cSSam Leffler 	case WSC_MSG:
149439beb93cSSam Leffler 	case WSC_UPnP:
149539beb93cSSam Leffler 		return wps_process_wsc_msg(wps, msg);
149639beb93cSSam Leffler 	case WSC_ACK:
1497f05cddf9SRui Paulo 		if (wps_validate_wsc_ack(msg) < 0)
1498f05cddf9SRui Paulo 			return WPS_FAILURE;
149939beb93cSSam Leffler 		return wps_process_wsc_ack(wps, msg);
150039beb93cSSam Leffler 	case WSC_NACK:
1501f05cddf9SRui Paulo 		if (wps_validate_wsc_nack(msg) < 0)
1502f05cddf9SRui Paulo 			return WPS_FAILURE;
150339beb93cSSam Leffler 		return wps_process_wsc_nack(wps, msg);
150439beb93cSSam Leffler 	default:
150539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
150639beb93cSSam Leffler 		return WPS_FAILURE;
150739beb93cSSam Leffler 	}
150839beb93cSSam Leffler }
1509