1189251Ssam/*
2189251Ssam * Wi-Fi Protected Setup - Enrollee
3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12214734Srpaulo#include "crypto/crypto.h"
13214734Srpaulo#include "crypto/sha256.h"
14252726Srpaulo#include "crypto/random.h"
15189251Ssam#include "wps_i.h"
16189251Ssam#include "wps_dev_attr.h"
17189251Ssam
18189251Ssam
19189251Ssamstatic int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
20189251Ssam{
21189251Ssam	u8 state;
22189251Ssam	if (wps->wps->ap)
23189251Ssam		state = wps->wps->wps_state;
24189251Ssam	else
25189251Ssam		state = WPS_STATE_NOT_CONFIGURED;
26189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
27189251Ssam		   state);
28189251Ssam	wpabuf_put_be16(msg, ATTR_WPS_STATE);
29189251Ssam	wpabuf_put_be16(msg, 1);
30209158Srpaulo	wpabuf_put_u8(msg, state);
31189251Ssam	return 0;
32189251Ssam}
33189251Ssam
34189251Ssam
35189251Ssamstatic int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
36189251Ssam{
37189251Ssam	u8 *hash;
38189251Ssam	const u8 *addr[4];
39189251Ssam	size_t len[4];
40189251Ssam
41252726Srpaulo	if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
42189251Ssam		return -1;
43189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
44189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
45189251Ssam		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
46189251Ssam
47189251Ssam	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
48189251Ssam		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
49189251Ssam			   "E-Hash derivation");
50189251Ssam		return -1;
51189251Ssam	}
52189251Ssam
53189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
54189251Ssam	wpabuf_put_be16(msg, ATTR_E_HASH1);
55189251Ssam	wpabuf_put_be16(msg, SHA256_MAC_LEN);
56189251Ssam	hash = wpabuf_put(msg, SHA256_MAC_LEN);
57189251Ssam	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
58189251Ssam	addr[0] = wps->snonce;
59189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
60189251Ssam	addr[1] = wps->psk1;
61189251Ssam	len[1] = WPS_PSK_LEN;
62189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
63189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
64189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
65189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
66189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
67189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
68189251Ssam
69189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
70189251Ssam	wpabuf_put_be16(msg, ATTR_E_HASH2);
71189251Ssam	wpabuf_put_be16(msg, SHA256_MAC_LEN);
72189251Ssam	hash = wpabuf_put(msg, SHA256_MAC_LEN);
73189251Ssam	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
74189251Ssam	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
75189251Ssam	addr[1] = wps->psk2;
76189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
77189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
78189251Ssam
79189251Ssam	return 0;
80189251Ssam}
81189251Ssam
82189251Ssam
83189251Ssamstatic int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
84189251Ssam{
85189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
86189251Ssam	wpabuf_put_be16(msg, ATTR_E_SNONCE1);
87189251Ssam	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
88189251Ssam	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
89189251Ssam	return 0;
90189251Ssam}
91189251Ssam
92189251Ssam
93189251Ssamstatic int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
94189251Ssam{
95189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
96189251Ssam	wpabuf_put_be16(msg, ATTR_E_SNONCE2);
97189251Ssam	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
98189251Ssam	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
99189251Ssam			WPS_SECRET_NONCE_LEN);
100189251Ssam	return 0;
101189251Ssam}
102189251Ssam
103189251Ssam
104189251Ssamstatic struct wpabuf * wps_build_m1(struct wps_data *wps)
105189251Ssam{
106189251Ssam	struct wpabuf *msg;
107252726Srpaulo	u16 config_methods;
108346981Scy	u8 multi_ap_backhaul_sta = 0;
109189251Ssam
110252726Srpaulo	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
111189251Ssam		return NULL;
112189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
113189251Ssam		    wps->nonce_e, WPS_NONCE_LEN);
114189251Ssam
115189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
116189251Ssam	msg = wpabuf_alloc(1000);
117189251Ssam	if (msg == NULL)
118189251Ssam		return NULL;
119189251Ssam
120252726Srpaulo	config_methods = wps->wps->config_methods;
121252726Srpaulo	if (wps->wps->ap && !wps->pbc_in_m1 &&
122252726Srpaulo	    (wps->dev_password_len != 0 ||
123252726Srpaulo	     (config_methods & WPS_CONFIG_DISPLAY))) {
124252726Srpaulo		/*
125252726Srpaulo		 * These are the methods that the AP supports as an Enrollee
126252726Srpaulo		 * for adding external Registrars, so remove PushButton.
127252726Srpaulo		 *
128252726Srpaulo		 * As a workaround for Windows 7 mechanism for probing WPS
129252726Srpaulo		 * capabilities from M1, leave PushButton option if no PIN
130252726Srpaulo		 * method is available or if WPS configuration enables PBC
131252726Srpaulo		 * workaround.
132252726Srpaulo		 */
133252726Srpaulo		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
134252726Srpaulo		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
135252726Srpaulo				    WPS_CONFIG_PHY_PUSHBUTTON);
136252726Srpaulo	}
137252726Srpaulo
138346981Scy	if (wps->multi_ap_backhaul_sta)
139346981Scy		multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA;
140346981Scy
141189251Ssam	if (wps_build_version(msg) ||
142189251Ssam	    wps_build_msg_type(msg, WPS_M1) ||
143189251Ssam	    wps_build_uuid_e(msg, wps->uuid_e) ||
144281806Srpaulo	    wps_build_mac_addr(msg, wps->mac_addr_e) ||
145189251Ssam	    wps_build_enrollee_nonce(wps, msg) ||
146189251Ssam	    wps_build_public_key(wps, msg) ||
147189251Ssam	    wps_build_auth_type_flags(wps, msg) ||
148189251Ssam	    wps_build_encr_type_flags(wps, msg) ||
149189251Ssam	    wps_build_conn_type_flags(wps, msg) ||
150252726Srpaulo	    wps_build_config_methods(msg, config_methods) ||
151189251Ssam	    wps_build_wps_state(wps, msg) ||
152189251Ssam	    wps_build_device_attrs(&wps->wps->dev, msg) ||
153281806Srpaulo	    wps_build_rf_bands(&wps->wps->dev, msg,
154281806Srpaulo			       wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
155189251Ssam	    wps_build_assoc_state(wps, msg) ||
156189251Ssam	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
157189251Ssam	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
158252726Srpaulo	    wps_build_os_version(&wps->wps->dev, msg) ||
159346981Scy	    wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) ||
160252726Srpaulo	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
161189251Ssam		wpabuf_free(msg);
162189251Ssam		return NULL;
163189251Ssam	}
164189251Ssam
165189251Ssam	wps->state = RECV_M2;
166189251Ssam	return msg;
167189251Ssam}
168189251Ssam
169189251Ssam
170189251Ssamstatic struct wpabuf * wps_build_m3(struct wps_data *wps)
171189251Ssam{
172189251Ssam	struct wpabuf *msg;
173189251Ssam
174189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
175189251Ssam
176189251Ssam	if (wps->dev_password == NULL) {
177189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
178189251Ssam		return NULL;
179189251Ssam	}
180337817Scy	if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0)
181337817Scy		return NULL;
182189251Ssam
183281806Srpaulo	if (wps->wps->ap && random_pool_ready() != 1) {
184281806Srpaulo		wpa_printf(MSG_INFO,
185281806Srpaulo			   "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
186281806Srpaulo		return NULL;
187281806Srpaulo	}
188281806Srpaulo
189189251Ssam	msg = wpabuf_alloc(1000);
190189251Ssam	if (msg == NULL)
191189251Ssam		return NULL;
192189251Ssam
193189251Ssam	if (wps_build_version(msg) ||
194189251Ssam	    wps_build_msg_type(msg, WPS_M3) ||
195189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
196189251Ssam	    wps_build_e_hash(wps, msg) ||
197346981Scy	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
198189251Ssam	    wps_build_authenticator(wps, msg)) {
199189251Ssam		wpabuf_free(msg);
200189251Ssam		return NULL;
201189251Ssam	}
202189251Ssam
203189251Ssam	wps->state = RECV_M4;
204189251Ssam	return msg;
205189251Ssam}
206189251Ssam
207189251Ssam
208189251Ssamstatic struct wpabuf * wps_build_m5(struct wps_data *wps)
209189251Ssam{
210189251Ssam	struct wpabuf *msg, *plain;
211189251Ssam
212189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
213189251Ssam
214189251Ssam	plain = wpabuf_alloc(200);
215189251Ssam	if (plain == NULL)
216189251Ssam		return NULL;
217189251Ssam
218189251Ssam	msg = wpabuf_alloc(1000);
219189251Ssam	if (msg == NULL) {
220189251Ssam		wpabuf_free(plain);
221189251Ssam		return NULL;
222189251Ssam	}
223189251Ssam
224189251Ssam	if (wps_build_version(msg) ||
225189251Ssam	    wps_build_msg_type(msg, WPS_M5) ||
226189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
227189251Ssam	    wps_build_e_snonce1(wps, plain) ||
228189251Ssam	    wps_build_key_wrap_auth(wps, plain) ||
229189251Ssam	    wps_build_encr_settings(wps, msg, plain) ||
230346981Scy	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
231189251Ssam	    wps_build_authenticator(wps, msg)) {
232337817Scy		wpabuf_clear_free(plain);
233189251Ssam		wpabuf_free(msg);
234189251Ssam		return NULL;
235189251Ssam	}
236337817Scy	wpabuf_clear_free(plain);
237189251Ssam
238189251Ssam	wps->state = RECV_M6;
239189251Ssam	return msg;
240189251Ssam}
241189251Ssam
242189251Ssam
243189251Ssamstatic int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
244189251Ssam{
245189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
246189251Ssam	wpabuf_put_be16(msg, ATTR_SSID);
247189251Ssam	wpabuf_put_be16(msg, wps->wps->ssid_len);
248189251Ssam	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
249189251Ssam	return 0;
250189251Ssam}
251189251Ssam
252189251Ssam
253189251Ssamstatic int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
254189251Ssam{
255281806Srpaulo	u16 auth_type = wps->wps->ap_auth_type;
256252726Srpaulo
257281806Srpaulo	/*
258281806Srpaulo	 * Work around issues with Windows 7 WPS implementation not liking
259281806Srpaulo	 * multiple Authentication Type bits in M7 AP Settings attribute by
260281806Srpaulo	 * showing only the most secure option from current configuration.
261281806Srpaulo	 */
262252726Srpaulo	if (auth_type & WPS_AUTH_WPA2PSK)
263252726Srpaulo		auth_type = WPS_AUTH_WPA2PSK;
264252726Srpaulo	else if (auth_type & WPS_AUTH_WPAPSK)
265252726Srpaulo		auth_type = WPS_AUTH_WPAPSK;
266252726Srpaulo	else if (auth_type & WPS_AUTH_OPEN)
267252726Srpaulo		auth_type = WPS_AUTH_OPEN;
268252726Srpaulo
269252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
270189251Ssam	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
271189251Ssam	wpabuf_put_be16(msg, 2);
272252726Srpaulo	wpabuf_put_be16(msg, auth_type);
273189251Ssam	return 0;
274189251Ssam}
275189251Ssam
276189251Ssam
277189251Ssamstatic int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
278189251Ssam{
279281806Srpaulo	u16 encr_type = wps->wps->ap_encr_type;
280252726Srpaulo
281281806Srpaulo	/*
282281806Srpaulo	 * Work around issues with Windows 7 WPS implementation not liking
283281806Srpaulo	 * multiple Encryption Type bits in M7 AP Settings attribute by
284281806Srpaulo	 * showing only the most secure option from current configuration.
285281806Srpaulo	 */
286281806Srpaulo	if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
287252726Srpaulo		if (encr_type & WPS_ENCR_AES)
288252726Srpaulo			encr_type = WPS_ENCR_AES;
289252726Srpaulo		else if (encr_type & WPS_ENCR_TKIP)
290252726Srpaulo			encr_type = WPS_ENCR_TKIP;
291252726Srpaulo	}
292252726Srpaulo
293252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
294189251Ssam	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
295189251Ssam	wpabuf_put_be16(msg, 2);
296252726Srpaulo	wpabuf_put_be16(msg, encr_type);
297189251Ssam	return 0;
298189251Ssam}
299189251Ssam
300189251Ssam
301189251Ssamstatic int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
302189251Ssam{
303281806Srpaulo	if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
304281806Srpaulo	    wps->wps->network_key_len == 0) {
305281806Srpaulo		char hex[65];
306281806Srpaulo		u8 psk[32];
307281806Srpaulo		/* Generate a random per-device PSK */
308281806Srpaulo		if (random_pool_ready() != 1 ||
309281806Srpaulo		    random_get_bytes(psk, sizeof(psk)) < 0) {
310281806Srpaulo			wpa_printf(MSG_INFO,
311281806Srpaulo				   "WPS: Could not generate random PSK");
312281806Srpaulo			return -1;
313281806Srpaulo		}
314281806Srpaulo		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
315281806Srpaulo				psk, sizeof(psk));
316281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
317281806Srpaulo			   (unsigned int) wps->new_psk_len * 2);
318281806Srpaulo		wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
319281806Srpaulo		wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
320281806Srpaulo		wpabuf_put_be16(msg, sizeof(psk) * 2);
321281806Srpaulo		wpabuf_put_data(msg, hex, sizeof(psk) * 2);
322281806Srpaulo		if (wps->wps->registrar) {
323281806Srpaulo			wps_cb_new_psk(wps->wps->registrar,
324281806Srpaulo				       wps->peer_dev.mac_addr,
325281806Srpaulo				       wps->p2p_dev_addr, psk, sizeof(psk));
326281806Srpaulo		}
327281806Srpaulo		return 0;
328281806Srpaulo	}
329281806Srpaulo
330281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
331281806Srpaulo		   (unsigned int) wps->wps->network_key_len);
332189251Ssam	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
333189251Ssam	wpabuf_put_be16(msg, wps->wps->network_key_len);
334189251Ssam	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
335189251Ssam	return 0;
336189251Ssam}
337189251Ssam
338189251Ssam
339189251Ssamstatic int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
340189251Ssam{
341189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
342189251Ssam	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
343189251Ssam	wpabuf_put_be16(msg, ETH_ALEN);
344189251Ssam	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
345189251Ssam	return 0;
346189251Ssam}
347189251Ssam
348189251Ssam
349189251Ssamstatic int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
350189251Ssam{
351281806Srpaulo	const u8 *start, *end;
352281806Srpaulo	int ret;
353281806Srpaulo
354189251Ssam	if (wps->wps->ap_settings) {
355189251Ssam		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
356189251Ssam		wpabuf_put_data(plain, wps->wps->ap_settings,
357189251Ssam				wps->wps->ap_settings_len);
358189251Ssam		return 0;
359189251Ssam	}
360189251Ssam
361281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * AP Settings based on current configuration");
362281806Srpaulo	start = wpabuf_put(plain, 0);
363281806Srpaulo	ret = wps_build_cred_ssid(wps, plain) ||
364189251Ssam		wps_build_cred_mac_addr(wps, plain) ||
365189251Ssam		wps_build_cred_auth_type(wps, plain) ||
366189251Ssam		wps_build_cred_encr_type(wps, plain) ||
367189251Ssam		wps_build_cred_network_key(wps, plain);
368281806Srpaulo	end = wpabuf_put(plain, 0);
369281806Srpaulo
370281806Srpaulo	wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
371281806Srpaulo			start, end - start);
372281806Srpaulo
373281806Srpaulo	return ret;
374189251Ssam}
375189251Ssam
376189251Ssam
377189251Ssamstatic struct wpabuf * wps_build_m7(struct wps_data *wps)
378189251Ssam{
379189251Ssam	struct wpabuf *msg, *plain;
380189251Ssam
381189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
382189251Ssam
383189251Ssam	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
384189251Ssam	if (plain == NULL)
385189251Ssam		return NULL;
386189251Ssam
387189251Ssam	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
388189251Ssam	if (msg == NULL) {
389189251Ssam		wpabuf_free(plain);
390189251Ssam		return NULL;
391189251Ssam	}
392189251Ssam
393189251Ssam	if (wps_build_version(msg) ||
394189251Ssam	    wps_build_msg_type(msg, WPS_M7) ||
395189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
396189251Ssam	    wps_build_e_snonce2(wps, plain) ||
397189251Ssam	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
398189251Ssam	    wps_build_key_wrap_auth(wps, plain) ||
399189251Ssam	    wps_build_encr_settings(wps, msg, plain) ||
400346981Scy	    wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
401189251Ssam	    wps_build_authenticator(wps, msg)) {
402337817Scy		wpabuf_clear_free(plain);
403189251Ssam		wpabuf_free(msg);
404189251Ssam		return NULL;
405189251Ssam	}
406337817Scy	wpabuf_clear_free(plain);
407189251Ssam
408214734Srpaulo	if (wps->wps->ap && wps->wps->registrar) {
409214734Srpaulo		/*
410214734Srpaulo		 * If the Registrar is only learning our current configuration,
411214734Srpaulo		 * it may not continue protocol run to successful completion.
412214734Srpaulo		 * Store information here to make sure it remains available.
413214734Srpaulo		 */
414214734Srpaulo		wps_device_store(wps->wps->registrar, &wps->peer_dev,
415214734Srpaulo				 wps->uuid_r);
416214734Srpaulo	}
417214734Srpaulo
418189251Ssam	wps->state = RECV_M8;
419189251Ssam	return msg;
420189251Ssam}
421189251Ssam
422189251Ssam
423189251Ssamstatic struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
424189251Ssam{
425189251Ssam	struct wpabuf *msg;
426189251Ssam
427189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
428189251Ssam
429189251Ssam	msg = wpabuf_alloc(1000);
430189251Ssam	if (msg == NULL)
431189251Ssam		return NULL;
432189251Ssam
433189251Ssam	if (wps_build_version(msg) ||
434189251Ssam	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
435189251Ssam	    wps_build_enrollee_nonce(wps, msg) ||
436252726Srpaulo	    wps_build_registrar_nonce(wps, msg) ||
437346981Scy	    wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
438189251Ssam		wpabuf_free(msg);
439189251Ssam		return NULL;
440189251Ssam	}
441189251Ssam
442189251Ssam	if (wps->wps->ap)
443189251Ssam		wps->state = RECV_ACK;
444189251Ssam	else {
445281806Srpaulo		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
446189251Ssam		wps->state = WPS_FINISHED;
447189251Ssam	}
448189251Ssam	return msg;
449189251Ssam}
450189251Ssam
451189251Ssam
452189251Ssamstruct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
453189251Ssam				     enum wsc_op_code *op_code)
454189251Ssam{
455189251Ssam	struct wpabuf *msg;
456189251Ssam
457189251Ssam	switch (wps->state) {
458189251Ssam	case SEND_M1:
459189251Ssam		msg = wps_build_m1(wps);
460189251Ssam		*op_code = WSC_MSG;
461189251Ssam		break;
462189251Ssam	case SEND_M3:
463189251Ssam		msg = wps_build_m3(wps);
464189251Ssam		*op_code = WSC_MSG;
465189251Ssam		break;
466189251Ssam	case SEND_M5:
467189251Ssam		msg = wps_build_m5(wps);
468189251Ssam		*op_code = WSC_MSG;
469189251Ssam		break;
470189251Ssam	case SEND_M7:
471189251Ssam		msg = wps_build_m7(wps);
472189251Ssam		*op_code = WSC_MSG;
473189251Ssam		break;
474189251Ssam	case RECEIVED_M2D:
475189251Ssam		if (wps->wps->ap) {
476189251Ssam			msg = wps_build_wsc_nack(wps);
477189251Ssam			*op_code = WSC_NACK;
478189251Ssam			break;
479189251Ssam		}
480189251Ssam		msg = wps_build_wsc_ack(wps);
481189251Ssam		*op_code = WSC_ACK;
482189251Ssam		if (msg) {
483189251Ssam			/* Another M2/M2D may be received */
484189251Ssam			wps->state = RECV_M2;
485189251Ssam		}
486189251Ssam		break;
487189251Ssam	case SEND_WSC_NACK:
488189251Ssam		msg = wps_build_wsc_nack(wps);
489189251Ssam		*op_code = WSC_NACK;
490189251Ssam		break;
491189251Ssam	case WPS_MSG_DONE:
492189251Ssam		msg = wps_build_wsc_done(wps);
493189251Ssam		*op_code = WSC_Done;
494189251Ssam		break;
495189251Ssam	default:
496189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
497189251Ssam			   "a message", wps->state);
498189251Ssam		msg = NULL;
499189251Ssam		break;
500189251Ssam	}
501189251Ssam
502189251Ssam	if (*op_code == WSC_MSG && msg) {
503189251Ssam		/* Save a copy of the last message for Authenticator derivation
504189251Ssam		 */
505189251Ssam		wpabuf_free(wps->last_msg);
506189251Ssam		wps->last_msg = wpabuf_dup(msg);
507189251Ssam	}
508189251Ssam
509189251Ssam	return msg;
510189251Ssam}
511189251Ssam
512189251Ssam
513189251Ssamstatic int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
514189251Ssam{
515189251Ssam	if (r_nonce == NULL) {
516189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
517189251Ssam		return -1;
518189251Ssam	}
519189251Ssam
520189251Ssam	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
521189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
522189251Ssam		    wps->nonce_r, WPS_NONCE_LEN);
523189251Ssam
524189251Ssam	return 0;
525189251Ssam}
526189251Ssam
527189251Ssam
528189251Ssamstatic int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
529189251Ssam{
530189251Ssam	if (e_nonce == NULL) {
531189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
532189251Ssam		return -1;
533189251Ssam	}
534189251Ssam
535189251Ssam	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
536189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
537189251Ssam		return -1;
538189251Ssam	}
539189251Ssam
540189251Ssam	return 0;
541189251Ssam}
542189251Ssam
543189251Ssam
544189251Ssamstatic int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
545189251Ssam{
546189251Ssam	if (uuid_r == NULL) {
547189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
548189251Ssam		return -1;
549189251Ssam	}
550189251Ssam
551189251Ssam	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
552189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
553189251Ssam
554189251Ssam	return 0;
555189251Ssam}
556189251Ssam
557189251Ssam
558189251Ssamstatic int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
559189251Ssam			      size_t pk_len)
560189251Ssam{
561189251Ssam	if (pk == NULL || pk_len == 0) {
562189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
563189251Ssam		return -1;
564189251Ssam	}
565189251Ssam
566281806Srpaulo	if (wps->peer_pubkey_hash_set) {
567281806Srpaulo		u8 hash[WPS_HASH_LEN];
568281806Srpaulo		sha256_vector(1, &pk, &pk_len, hash);
569281806Srpaulo		if (os_memcmp_const(hash, wps->peer_pubkey_hash,
570281806Srpaulo				    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
571281806Srpaulo			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
572281806Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
573281806Srpaulo				    pk, pk_len);
574281806Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
575281806Srpaulo				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
576281806Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
577281806Srpaulo				    wps->peer_pubkey_hash,
578281806Srpaulo				    WPS_OOB_PUBKEY_HASH_LEN);
579281806Srpaulo			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
580281806Srpaulo			return -1;
581281806Srpaulo		}
582281806Srpaulo	}
583281806Srpaulo
584189251Ssam	wpabuf_free(wps->dh_pubkey_r);
585189251Ssam	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
586189251Ssam	if (wps->dh_pubkey_r == NULL)
587189251Ssam		return -1;
588189251Ssam
589189251Ssam	if (wps_derive_keys(wps) < 0)
590189251Ssam		return -1;
591189251Ssam
592189251Ssam	return 0;
593189251Ssam}
594189251Ssam
595189251Ssam
596189251Ssamstatic int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
597189251Ssam{
598189251Ssam	if (r_hash1 == NULL) {
599189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
600189251Ssam		return -1;
601189251Ssam	}
602189251Ssam
603189251Ssam	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
604189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
605189251Ssam
606189251Ssam	return 0;
607189251Ssam}
608189251Ssam
609189251Ssam
610189251Ssamstatic int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
611189251Ssam{
612189251Ssam	if (r_hash2 == NULL) {
613189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
614189251Ssam		return -1;
615189251Ssam	}
616189251Ssam
617189251Ssam	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
618189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
619189251Ssam
620189251Ssam	return 0;
621189251Ssam}
622189251Ssam
623189251Ssam
624189251Ssamstatic int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
625189251Ssam{
626189251Ssam	u8 hash[SHA256_MAC_LEN];
627189251Ssam	const u8 *addr[4];
628189251Ssam	size_t len[4];
629189251Ssam
630189251Ssam	if (r_snonce1 == NULL) {
631189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
632189251Ssam		return -1;
633189251Ssam	}
634189251Ssam
635189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
636189251Ssam			WPS_SECRET_NONCE_LEN);
637189251Ssam
638189251Ssam	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
639189251Ssam	addr[0] = r_snonce1;
640189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
641189251Ssam	addr[1] = wps->psk1;
642189251Ssam	len[1] = WPS_PSK_LEN;
643189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
644189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
645189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
646189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
647189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
648189251Ssam
649281806Srpaulo	if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
650189251Ssam		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
651189251Ssam			   "not match with the pre-committed value");
652189251Ssam		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
653281806Srpaulo		wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr);
654189251Ssam		return -1;
655189251Ssam	}
656189251Ssam
657189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
658189251Ssam		   "half of the device password");
659189251Ssam
660189251Ssam	return 0;
661189251Ssam}
662189251Ssam
663189251Ssam
664189251Ssamstatic int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
665189251Ssam{
666189251Ssam	u8 hash[SHA256_MAC_LEN];
667189251Ssam	const u8 *addr[4];
668189251Ssam	size_t len[4];
669189251Ssam
670189251Ssam	if (r_snonce2 == NULL) {
671189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
672189251Ssam		return -1;
673189251Ssam	}
674189251Ssam
675189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
676189251Ssam			WPS_SECRET_NONCE_LEN);
677189251Ssam
678189251Ssam	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
679189251Ssam	addr[0] = r_snonce2;
680189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
681189251Ssam	addr[1] = wps->psk2;
682189251Ssam	len[1] = WPS_PSK_LEN;
683189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
684189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
685189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
686189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
687189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
688189251Ssam
689281806Srpaulo	if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
690189251Ssam		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
691189251Ssam			   "not match with the pre-committed value");
692189251Ssam		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
693281806Srpaulo		wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr);
694189251Ssam		return -1;
695189251Ssam	}
696189251Ssam
697189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
698189251Ssam		   "half of the device password");
699189251Ssam
700189251Ssam	return 0;
701189251Ssam}
702189251Ssam
703189251Ssam
704189251Ssamstatic int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
705252726Srpaulo			      size_t cred_len, int wps2)
706189251Ssam{
707189251Ssam	struct wps_parse_attr attr;
708189251Ssam	struct wpabuf msg;
709252726Srpaulo	int ret = 0;
710189251Ssam
711189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
712189251Ssam	os_memset(&wps->cred, 0, sizeof(wps->cred));
713189251Ssam	wpabuf_set(&msg, cred, cred_len);
714189251Ssam	if (wps_parse_msg(&msg, &attr) < 0 ||
715189251Ssam	    wps_process_cred(&attr, &wps->cred))
716189251Ssam		return -1;
717189251Ssam
718209158Srpaulo	if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
719209158Srpaulo	    0) {
720209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
721209158Srpaulo			   MACSTR ") does not match with own address (" MACSTR
722209158Srpaulo			   ")", MAC2STR(wps->cred.mac_addr),
723209158Srpaulo			   MAC2STR(wps->wps->dev.mac_addr));
724209158Srpaulo		/*
725209158Srpaulo		 * In theory, this could be consider fatal error, but there are
726209158Srpaulo		 * number of deployed implementations using other address here
727209158Srpaulo		 * due to unclarity in the specification. For interoperability
728209158Srpaulo		 * reasons, allow this to be processed since we do not really
729209158Srpaulo		 * use the MAC Address information for anything.
730209158Srpaulo		 */
731252726Srpaulo#ifdef CONFIG_WPS_STRICT
732252726Srpaulo		if (wps2) {
733252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
734252726Srpaulo				   "MAC Address in AP Settings");
735252726Srpaulo			return -1;
736252726Srpaulo		}
737252726Srpaulo#endif /* CONFIG_WPS_STRICT */
738209158Srpaulo	}
739209158Srpaulo
740252726Srpaulo	if (!(wps->cred.encr_type &
741252726Srpaulo	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
742252726Srpaulo		if (wps->cred.encr_type & WPS_ENCR_WEP) {
743252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Reject Credential "
744252726Srpaulo				   "due to WEP configuration");
745252726Srpaulo			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
746252726Srpaulo			return -2;
747252726Srpaulo		}
748252726Srpaulo
749252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
750252726Srpaulo			   "invalid encr_type 0x%x", wps->cred.encr_type);
751252726Srpaulo		return -1;
752252726Srpaulo	}
753252726Srpaulo
754189251Ssam	if (wps->wps->cred_cb) {
755189251Ssam		wps->cred.cred_attr = cred - 4;
756189251Ssam		wps->cred.cred_attr_len = cred_len + 4;
757252726Srpaulo		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
758189251Ssam		wps->cred.cred_attr = NULL;
759189251Ssam		wps->cred.cred_attr_len = 0;
760189251Ssam	}
761189251Ssam
762252726Srpaulo	return ret;
763189251Ssam}
764189251Ssam
765189251Ssam
766189251Ssamstatic int wps_process_creds(struct wps_data *wps, const u8 *cred[],
767289549Srpaulo			     u16 cred_len[], unsigned int num_cred, int wps2)
768189251Ssam{
769189251Ssam	size_t i;
770252726Srpaulo	int ok = 0;
771189251Ssam
772189251Ssam	if (wps->wps->ap)
773189251Ssam		return 0;
774189251Ssam
775189251Ssam	if (num_cred == 0) {
776189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
777189251Ssam			   "received");
778189251Ssam		return -1;
779189251Ssam	}
780189251Ssam
781189251Ssam	for (i = 0; i < num_cred; i++) {
782252726Srpaulo		int res;
783252726Srpaulo		res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
784252726Srpaulo		if (res == 0)
785252726Srpaulo			ok++;
786252726Srpaulo		else if (res == -2)
787252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
788252726Srpaulo		else
789189251Ssam			return -1;
790189251Ssam	}
791189251Ssam
792252726Srpaulo	if (ok == 0) {
793252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
794252726Srpaulo			   "received");
795252726Srpaulo		return -1;
796252726Srpaulo	}
797252726Srpaulo
798189251Ssam	return 0;
799189251Ssam}
800189251Ssam
801189251Ssam
802189251Ssamstatic int wps_process_ap_settings_e(struct wps_data *wps,
803189251Ssam				     struct wps_parse_attr *attr,
804252726Srpaulo				     struct wpabuf *attrs, int wps2)
805189251Ssam{
806189251Ssam	struct wps_credential cred;
807289549Srpaulo	int ret = 0;
808189251Ssam
809189251Ssam	if (!wps->wps->ap)
810189251Ssam		return 0;
811189251Ssam
812189251Ssam	if (wps_process_ap_settings(attr, &cred) < 0)
813189251Ssam		return -1;
814189251Ssam
815189251Ssam	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
816189251Ssam		   "Registrar");
817189251Ssam
818209158Srpaulo	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
819209158Srpaulo	    0) {
820209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
821209158Srpaulo			   MACSTR ") does not match with own address (" MACSTR
822209158Srpaulo			   ")", MAC2STR(cred.mac_addr),
823209158Srpaulo			   MAC2STR(wps->wps->dev.mac_addr));
824209158Srpaulo		/*
825209158Srpaulo		 * In theory, this could be consider fatal error, but there are
826209158Srpaulo		 * number of deployed implementations using other address here
827209158Srpaulo		 * due to unclarity in the specification. For interoperability
828209158Srpaulo		 * reasons, allow this to be processed since we do not really
829209158Srpaulo		 * use the MAC Address information for anything.
830209158Srpaulo		 */
831252726Srpaulo#ifdef CONFIG_WPS_STRICT
832252726Srpaulo		if (wps2) {
833252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
834252726Srpaulo				   "MAC Address in AP Settings");
835252726Srpaulo			return -1;
836252726Srpaulo		}
837252726Srpaulo#endif /* CONFIG_WPS_STRICT */
838209158Srpaulo	}
839209158Srpaulo
840252726Srpaulo	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
841252726Srpaulo	{
842252726Srpaulo		if (cred.encr_type & WPS_ENCR_WEP) {
843252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
844252726Srpaulo				   "due to WEP configuration");
845252726Srpaulo			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
846252726Srpaulo			return -1;
847252726Srpaulo		}
848252726Srpaulo
849252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
850252726Srpaulo			   "invalid encr_type 0x%x", cred.encr_type);
851252726Srpaulo		return -1;
852252726Srpaulo	}
853252726Srpaulo
854252726Srpaulo#ifdef CONFIG_WPS_STRICT
855252726Srpaulo	if (wps2) {
856252726Srpaulo		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
857252726Srpaulo		    WPS_ENCR_TKIP ||
858252726Srpaulo		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
859252726Srpaulo		    WPS_AUTH_WPAPSK) {
860252726Srpaulo			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
861252726Srpaulo				   "AP Settings: WPA-Personal/TKIP only");
862252726Srpaulo			wps->error_indication =
863252726Srpaulo				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
864252726Srpaulo			return -1;
865252726Srpaulo		}
866252726Srpaulo	}
867252726Srpaulo#endif /* CONFIG_WPS_STRICT */
868252726Srpaulo
869252726Srpaulo	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
870252726Srpaulo	{
871252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
872252726Srpaulo			   "TKIP+AES");
873252726Srpaulo		cred.encr_type |= WPS_ENCR_AES;
874252726Srpaulo	}
875252726Srpaulo
876252726Srpaulo	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
877252726Srpaulo	    WPS_AUTH_WPAPSK) {
878252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
879252726Srpaulo			   "WPAPSK+WPA2PSK");
880252726Srpaulo		cred.auth_type |= WPS_AUTH_WPA2PSK;
881252726Srpaulo	}
882252726Srpaulo
883189251Ssam	if (wps->wps->cred_cb) {
884189251Ssam		cred.cred_attr = wpabuf_head(attrs);
885189251Ssam		cred.cred_attr_len = wpabuf_len(attrs);
886289549Srpaulo		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
887189251Ssam	}
888189251Ssam
889289549Srpaulo	return ret;
890189251Ssam}
891189251Ssam
892189251Ssam
893281806Srpaulostatic int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
894281806Srpaulo{
895281806Srpaulo	u16 id;
896281806Srpaulo
897281806Srpaulo	if (dev_pw_id == NULL) {
898281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
899281806Srpaulo		return -1;
900281806Srpaulo	}
901281806Srpaulo
902281806Srpaulo	id = WPA_GET_BE16(dev_pw_id);
903281806Srpaulo	if (wps->dev_pw_id == id) {
904281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
905281806Srpaulo		return 0;
906281806Srpaulo	}
907281806Srpaulo
908281806Srpaulo#ifdef CONFIG_P2P
909281806Srpaulo	if ((id == DEV_PW_DEFAULT &&
910281806Srpaulo	     wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
911281806Srpaulo	    (id == DEV_PW_REGISTRAR_SPECIFIED &&
912281806Srpaulo	     wps->dev_pw_id == DEV_PW_DEFAULT)) {
913281806Srpaulo		/*
914281806Srpaulo		 * Common P2P use cases indicate whether the PIN is from the
915281806Srpaulo		 * client or GO using Device Password Id in M1/M2 in a way that
916281806Srpaulo		 * does not look fully compliant with WSC specification. Anyway,
917281806Srpaulo		 * this is deployed and needs to be allowed, so ignore changes
918281806Srpaulo		 * between Registrar-Specified and Default PIN.
919281806Srpaulo		 */
920281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
921281806Srpaulo			   "change");
922281806Srpaulo		return 0;
923281806Srpaulo	}
924281806Srpaulo#endif /* CONFIG_P2P */
925281806Srpaulo
926281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
927281806Srpaulo		   "ID from %u to %u", wps->dev_pw_id, id);
928281806Srpaulo
929281806Srpaulo	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
930281806Srpaulo		wpa_printf(MSG_DEBUG,
931281806Srpaulo			   "WPS: Workaround - ignore PBC-to-PIN change");
932281806Srpaulo		return 0;
933281806Srpaulo	}
934281806Srpaulo
935281806Srpaulo	if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
936281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
937281806Srpaulo		bin_clear_free(wps->dev_password, wps->dev_password_len);
938281806Srpaulo		wps->dev_pw_id = wps->alt_dev_pw_id;
939281806Srpaulo		wps->dev_password = wps->alt_dev_password;
940281806Srpaulo		wps->dev_password_len = wps->alt_dev_password_len;
941281806Srpaulo		wps->alt_dev_password = NULL;
942281806Srpaulo		wps->alt_dev_password_len = 0;
943281806Srpaulo		return 0;
944281806Srpaulo	}
945281806Srpaulo
946281806Srpaulo	return -1;
947281806Srpaulo}
948281806Srpaulo
949281806Srpaulo
950189251Ssamstatic enum wps_process_res wps_process_m2(struct wps_data *wps,
951189251Ssam					   const struct wpabuf *msg,
952189251Ssam					   struct wps_parse_attr *attr)
953189251Ssam{
954189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M2");
955189251Ssam
956189251Ssam	if (wps->state != RECV_M2) {
957189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
958189251Ssam			   "receiving M2", wps->state);
959189251Ssam		wps->state = SEND_WSC_NACK;
960189251Ssam		return WPS_CONTINUE;
961189251Ssam	}
962189251Ssam
963189251Ssam	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
964189251Ssam	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
965281806Srpaulo	    wps_process_uuid_r(wps, attr->uuid_r) ||
966281806Srpaulo	    wps_process_dev_pw_id(wps, attr->dev_password_id)) {
967189251Ssam		wps->state = SEND_WSC_NACK;
968189251Ssam		return WPS_CONTINUE;
969189251Ssam	}
970189251Ssam
971252726Srpaulo	/*
972252726Srpaulo	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
973252726Srpaulo	 * special locked mode is used to allow protocol run up to M7 in order
974252726Srpaulo	 * to support external Registrars that only learn the current AP
975252726Srpaulo	 * configuration without changing it.
976252726Srpaulo	 */
977214734Srpaulo	if (wps->wps->ap &&
978252726Srpaulo	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
979252726Srpaulo	     wps->dev_password == NULL)) {
980189251Ssam		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
981189251Ssam			   "registration of a new Registrar");
982189251Ssam		wps->config_error = WPS_CFG_SETUP_LOCKED;
983189251Ssam		wps->state = SEND_WSC_NACK;
984189251Ssam		return WPS_CONTINUE;
985189251Ssam	}
986189251Ssam
987214734Srpaulo	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
988214734Srpaulo	    wps_process_authenticator(wps, attr->authenticator, msg) ||
989214734Srpaulo	    wps_process_device_attrs(&wps->peer_dev, attr)) {
990214734Srpaulo		wps->state = SEND_WSC_NACK;
991214734Srpaulo		return WPS_CONTINUE;
992214734Srpaulo	}
993214734Srpaulo
994281806Srpaulo#ifdef CONFIG_WPS_NFC
995281806Srpaulo	if (wps->peer_pubkey_hash_set) {
996281806Srpaulo		struct wpabuf *decrypted;
997281806Srpaulo		struct wps_parse_attr eattr;
998281806Srpaulo
999281806Srpaulo		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1000281806Srpaulo						      attr->encr_settings_len);
1001281806Srpaulo		if (decrypted == NULL) {
1002281806Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
1003281806Srpaulo				   "Encrypted Settings attribute");
1004281806Srpaulo			wps->state = SEND_WSC_NACK;
1005281806Srpaulo			return WPS_CONTINUE;
1006281806Srpaulo		}
1007281806Srpaulo
1008281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
1009281806Srpaulo			   "Settings attribute");
1010281806Srpaulo		if (wps_parse_msg(decrypted, &eattr) < 0 ||
1011281806Srpaulo		    wps_process_key_wrap_auth(wps, decrypted,
1012281806Srpaulo					      eattr.key_wrap_auth) ||
1013281806Srpaulo		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1014281806Srpaulo				      eattr.num_cred, attr->version2 != NULL)) {
1015337817Scy			wpabuf_clear_free(decrypted);
1016281806Srpaulo			wps->state = SEND_WSC_NACK;
1017281806Srpaulo			return WPS_CONTINUE;
1018281806Srpaulo		}
1019337817Scy		wpabuf_clear_free(decrypted);
1020281806Srpaulo
1021281806Srpaulo		wps->state = WPS_MSG_DONE;
1022281806Srpaulo		return WPS_CONTINUE;
1023281806Srpaulo	}
1024281806Srpaulo#endif /* CONFIG_WPS_NFC */
1025281806Srpaulo
1026189251Ssam	wps->state = SEND_M3;
1027189251Ssam	return WPS_CONTINUE;
1028189251Ssam}
1029189251Ssam
1030189251Ssam
1031189251Ssamstatic enum wps_process_res wps_process_m2d(struct wps_data *wps,
1032189251Ssam					    struct wps_parse_attr *attr)
1033189251Ssam{
1034189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
1035189251Ssam
1036189251Ssam	if (wps->state != RECV_M2) {
1037189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1038189251Ssam			   "receiving M2D", wps->state);
1039189251Ssam		wps->state = SEND_WSC_NACK;
1040189251Ssam		return WPS_CONTINUE;
1041189251Ssam	}
1042189251Ssam
1043189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
1044189251Ssam			  attr->manufacturer, attr->manufacturer_len);
1045189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
1046189251Ssam			  attr->model_name, attr->model_name_len);
1047189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
1048189251Ssam			  attr->model_number, attr->model_number_len);
1049189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
1050189251Ssam			  attr->serial_number, attr->serial_number_len);
1051189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
1052189251Ssam			  attr->dev_name, attr->dev_name_len);
1053189251Ssam
1054189251Ssam	if (wps->wps->event_cb) {
1055189251Ssam		union wps_event_data data;
1056189251Ssam		struct wps_event_m2d *m2d = &data.m2d;
1057189251Ssam		os_memset(&data, 0, sizeof(data));
1058189251Ssam		if (attr->config_methods)
1059189251Ssam			m2d->config_methods =
1060189251Ssam				WPA_GET_BE16(attr->config_methods);
1061189251Ssam		m2d->manufacturer = attr->manufacturer;
1062189251Ssam		m2d->manufacturer_len = attr->manufacturer_len;
1063189251Ssam		m2d->model_name = attr->model_name;
1064189251Ssam		m2d->model_name_len = attr->model_name_len;
1065189251Ssam		m2d->model_number = attr->model_number;
1066189251Ssam		m2d->model_number_len = attr->model_number_len;
1067189251Ssam		m2d->serial_number = attr->serial_number;
1068189251Ssam		m2d->serial_number_len = attr->serial_number_len;
1069189251Ssam		m2d->dev_name = attr->dev_name;
1070189251Ssam		m2d->dev_name_len = attr->dev_name_len;
1071189251Ssam		m2d->primary_dev_type = attr->primary_dev_type;
1072189251Ssam		if (attr->config_error)
1073189251Ssam			m2d->config_error =
1074189251Ssam				WPA_GET_BE16(attr->config_error);
1075189251Ssam		if (attr->dev_password_id)
1076189251Ssam			m2d->dev_password_id =
1077189251Ssam				WPA_GET_BE16(attr->dev_password_id);
1078189251Ssam		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
1079189251Ssam	}
1080189251Ssam
1081189251Ssam	wps->state = RECEIVED_M2D;
1082189251Ssam	return WPS_CONTINUE;
1083189251Ssam}
1084189251Ssam
1085189251Ssam
1086189251Ssamstatic enum wps_process_res wps_process_m4(struct wps_data *wps,
1087189251Ssam					   const struct wpabuf *msg,
1088189251Ssam					   struct wps_parse_attr *attr)
1089189251Ssam{
1090189251Ssam	struct wpabuf *decrypted;
1091189251Ssam	struct wps_parse_attr eattr;
1092189251Ssam
1093189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M4");
1094189251Ssam
1095189251Ssam	if (wps->state != RECV_M4) {
1096189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1097189251Ssam			   "receiving M4", wps->state);
1098189251Ssam		wps->state = SEND_WSC_NACK;
1099189251Ssam		return WPS_CONTINUE;
1100189251Ssam	}
1101189251Ssam
1102189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1103189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg) ||
1104189251Ssam	    wps_process_r_hash1(wps, attr->r_hash1) ||
1105189251Ssam	    wps_process_r_hash2(wps, attr->r_hash2)) {
1106189251Ssam		wps->state = SEND_WSC_NACK;
1107189251Ssam		return WPS_CONTINUE;
1108189251Ssam	}
1109189251Ssam
1110189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1111189251Ssam					      attr->encr_settings_len);
1112189251Ssam	if (decrypted == NULL) {
1113189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1114189251Ssam			   "Settings attribute");
1115189251Ssam		wps->state = SEND_WSC_NACK;
1116189251Ssam		return WPS_CONTINUE;
1117189251Ssam	}
1118189251Ssam
1119252726Srpaulo	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
1120337817Scy		wpabuf_clear_free(decrypted);
1121252726Srpaulo		wps->state = SEND_WSC_NACK;
1122252726Srpaulo		return WPS_CONTINUE;
1123252726Srpaulo	}
1124252726Srpaulo
1125189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1126189251Ssam		   "attribute");
1127189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1128189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1129189251Ssam	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
1130337817Scy		wpabuf_clear_free(decrypted);
1131189251Ssam		wps->state = SEND_WSC_NACK;
1132189251Ssam		return WPS_CONTINUE;
1133189251Ssam	}
1134337817Scy	wpabuf_clear_free(decrypted);
1135189251Ssam
1136189251Ssam	wps->state = SEND_M5;
1137189251Ssam	return WPS_CONTINUE;
1138189251Ssam}
1139189251Ssam
1140189251Ssam
1141189251Ssamstatic enum wps_process_res wps_process_m6(struct wps_data *wps,
1142189251Ssam					   const struct wpabuf *msg,
1143189251Ssam					   struct wps_parse_attr *attr)
1144189251Ssam{
1145189251Ssam	struct wpabuf *decrypted;
1146189251Ssam	struct wps_parse_attr eattr;
1147189251Ssam
1148189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M6");
1149189251Ssam
1150189251Ssam	if (wps->state != RECV_M6) {
1151189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1152189251Ssam			   "receiving M6", wps->state);
1153189251Ssam		wps->state = SEND_WSC_NACK;
1154189251Ssam		return WPS_CONTINUE;
1155189251Ssam	}
1156189251Ssam
1157189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1158189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg)) {
1159189251Ssam		wps->state = SEND_WSC_NACK;
1160189251Ssam		return WPS_CONTINUE;
1161189251Ssam	}
1162189251Ssam
1163189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1164189251Ssam					      attr->encr_settings_len);
1165189251Ssam	if (decrypted == NULL) {
1166189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1167189251Ssam			   "Settings attribute");
1168189251Ssam		wps->state = SEND_WSC_NACK;
1169189251Ssam		return WPS_CONTINUE;
1170189251Ssam	}
1171189251Ssam
1172252726Srpaulo	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1173337817Scy		wpabuf_clear_free(decrypted);
1174252726Srpaulo		wps->state = SEND_WSC_NACK;
1175252726Srpaulo		return WPS_CONTINUE;
1176252726Srpaulo	}
1177252726Srpaulo
1178189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1179189251Ssam		   "attribute");
1180189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1181189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1182189251Ssam	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
1183337817Scy		wpabuf_clear_free(decrypted);
1184189251Ssam		wps->state = SEND_WSC_NACK;
1185189251Ssam		return WPS_CONTINUE;
1186189251Ssam	}
1187337817Scy	wpabuf_clear_free(decrypted);
1188189251Ssam
1189252726Srpaulo	if (wps->wps->ap)
1190252726Srpaulo		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1191252726Srpaulo				   NULL);
1192252726Srpaulo
1193189251Ssam	wps->state = SEND_M7;
1194189251Ssam	return WPS_CONTINUE;
1195189251Ssam}
1196189251Ssam
1197189251Ssam
1198189251Ssamstatic enum wps_process_res wps_process_m8(struct wps_data *wps,
1199189251Ssam					   const struct wpabuf *msg,
1200189251Ssam					   struct wps_parse_attr *attr)
1201189251Ssam{
1202189251Ssam	struct wpabuf *decrypted;
1203189251Ssam	struct wps_parse_attr eattr;
1204189251Ssam
1205189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M8");
1206189251Ssam
1207189251Ssam	if (wps->state != RECV_M8) {
1208189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1209189251Ssam			   "receiving M8", wps->state);
1210189251Ssam		wps->state = SEND_WSC_NACK;
1211189251Ssam		return WPS_CONTINUE;
1212189251Ssam	}
1213189251Ssam
1214189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1215189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg)) {
1216189251Ssam		wps->state = SEND_WSC_NACK;
1217189251Ssam		return WPS_CONTINUE;
1218189251Ssam	}
1219189251Ssam
1220252726Srpaulo	if (wps->wps->ap && wps->wps->ap_setup_locked) {
1221252726Srpaulo		/*
1222252726Srpaulo		 * Stop here if special ap_setup_locked == 2 mode allowed the
1223252726Srpaulo		 * protocol to continue beyond M2. This allows ER to learn the
1224252726Srpaulo		 * current AP settings without changing them.
1225252726Srpaulo		 */
1226252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1227252726Srpaulo			   "registration of a new Registrar");
1228252726Srpaulo		wps->config_error = WPS_CFG_SETUP_LOCKED;
1229252726Srpaulo		wps->state = SEND_WSC_NACK;
1230252726Srpaulo		return WPS_CONTINUE;
1231252726Srpaulo	}
1232252726Srpaulo
1233189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1234189251Ssam					      attr->encr_settings_len);
1235189251Ssam	if (decrypted == NULL) {
1236189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1237189251Ssam			   "Settings attribute");
1238189251Ssam		wps->state = SEND_WSC_NACK;
1239189251Ssam		return WPS_CONTINUE;
1240189251Ssam	}
1241189251Ssam
1242252726Srpaulo	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1243252726Srpaulo				 attr->version2 != NULL) < 0) {
1244337817Scy		wpabuf_clear_free(decrypted);
1245252726Srpaulo		wps->state = SEND_WSC_NACK;
1246252726Srpaulo		return WPS_CONTINUE;
1247252726Srpaulo	}
1248252726Srpaulo
1249189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1250189251Ssam		   "attribute");
1251189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1252189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1253189251Ssam	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1254252726Srpaulo			      eattr.num_cred, attr->version2 != NULL) ||
1255252726Srpaulo	    wps_process_ap_settings_e(wps, &eattr, decrypted,
1256252726Srpaulo				      attr->version2 != NULL)) {
1257337817Scy		wpabuf_clear_free(decrypted);
1258189251Ssam		wps->state = SEND_WSC_NACK;
1259189251Ssam		return WPS_CONTINUE;
1260189251Ssam	}
1261337817Scy	wpabuf_clear_free(decrypted);
1262189251Ssam
1263189251Ssam	wps->state = WPS_MSG_DONE;
1264189251Ssam	return WPS_CONTINUE;
1265189251Ssam}
1266189251Ssam
1267189251Ssam
1268189251Ssamstatic enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
1269189251Ssam						const struct wpabuf *msg)
1270189251Ssam{
1271189251Ssam	struct wps_parse_attr attr;
1272189251Ssam	enum wps_process_res ret = WPS_CONTINUE;
1273189251Ssam
1274189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
1275189251Ssam
1276189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1277189251Ssam		return WPS_FAILURE;
1278189251Ssam
1279189251Ssam	if (attr.enrollee_nonce == NULL ||
1280252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1281189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1282189251Ssam		return WPS_FAILURE;
1283189251Ssam	}
1284189251Ssam
1285189251Ssam	if (attr.msg_type == NULL) {
1286189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1287252726Srpaulo		wps->state = SEND_WSC_NACK;
1288252726Srpaulo		return WPS_CONTINUE;
1289189251Ssam	}
1290189251Ssam
1291189251Ssam	switch (*attr.msg_type) {
1292189251Ssam	case WPS_M2:
1293252726Srpaulo		if (wps_validate_m2(msg) < 0)
1294252726Srpaulo			return WPS_FAILURE;
1295189251Ssam		ret = wps_process_m2(wps, msg, &attr);
1296189251Ssam		break;
1297189251Ssam	case WPS_M2D:
1298252726Srpaulo		if (wps_validate_m2d(msg) < 0)
1299252726Srpaulo			return WPS_FAILURE;
1300189251Ssam		ret = wps_process_m2d(wps, &attr);
1301189251Ssam		break;
1302189251Ssam	case WPS_M4:
1303252726Srpaulo		if (wps_validate_m4(msg) < 0)
1304252726Srpaulo			return WPS_FAILURE;
1305189251Ssam		ret = wps_process_m4(wps, msg, &attr);
1306189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1307252726Srpaulo			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
1308281806Srpaulo				       wps->error_indication,
1309281806Srpaulo				       wps->peer_dev.mac_addr);
1310189251Ssam		break;
1311189251Ssam	case WPS_M6:
1312252726Srpaulo		if (wps_validate_m6(msg) < 0)
1313252726Srpaulo			return WPS_FAILURE;
1314189251Ssam		ret = wps_process_m6(wps, msg, &attr);
1315189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1316252726Srpaulo			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
1317281806Srpaulo				       wps->error_indication,
1318281806Srpaulo				       wps->peer_dev.mac_addr);
1319189251Ssam		break;
1320189251Ssam	case WPS_M8:
1321252726Srpaulo		if (wps_validate_m8(msg) < 0)
1322252726Srpaulo			return WPS_FAILURE;
1323189251Ssam		ret = wps_process_m8(wps, msg, &attr);
1324189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1325252726Srpaulo			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
1326281806Srpaulo				       wps->error_indication,
1327281806Srpaulo				       wps->peer_dev.mac_addr);
1328189251Ssam		break;
1329189251Ssam	default:
1330189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
1331189251Ssam			   *attr.msg_type);
1332189251Ssam		return WPS_FAILURE;
1333189251Ssam	}
1334189251Ssam
1335189251Ssam	/*
1336189251Ssam	 * Save a copy of the last message for Authenticator derivation if we
1337189251Ssam	 * are continuing. However, skip M2D since it is not authenticated and
1338189251Ssam	 * neither is the ACK/NACK response frame. This allows the possibly
1339189251Ssam	 * following M2 to be processed correctly by using the previously sent
1340189251Ssam	 * M1 in Authenticator derivation.
1341189251Ssam	 */
1342189251Ssam	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
1343189251Ssam		/* Save a copy of the last message for Authenticator derivation
1344189251Ssam		 */
1345189251Ssam		wpabuf_free(wps->last_msg);
1346189251Ssam		wps->last_msg = wpabuf_dup(msg);
1347189251Ssam	}
1348189251Ssam
1349189251Ssam	return ret;
1350189251Ssam}
1351189251Ssam
1352189251Ssam
1353189251Ssamstatic enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
1354189251Ssam						const struct wpabuf *msg)
1355189251Ssam{
1356189251Ssam	struct wps_parse_attr attr;
1357189251Ssam
1358189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
1359189251Ssam
1360189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1361189251Ssam		return WPS_FAILURE;
1362189251Ssam
1363189251Ssam	if (attr.msg_type == NULL) {
1364189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1365189251Ssam		return WPS_FAILURE;
1366189251Ssam	}
1367189251Ssam
1368189251Ssam	if (*attr.msg_type != WPS_WSC_ACK) {
1369189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1370189251Ssam			   *attr.msg_type);
1371189251Ssam		return WPS_FAILURE;
1372189251Ssam	}
1373189251Ssam
1374189251Ssam	if (attr.registrar_nonce == NULL ||
1375252726Srpaulo	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1376189251Ssam	{
1377189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1378189251Ssam		return WPS_FAILURE;
1379189251Ssam	}
1380189251Ssam
1381189251Ssam	if (attr.enrollee_nonce == NULL ||
1382252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1383189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1384189251Ssam		return WPS_FAILURE;
1385189251Ssam	}
1386189251Ssam
1387189251Ssam	if (wps->state == RECV_ACK && wps->wps->ap) {
1388189251Ssam		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
1389189251Ssam			   "completed successfully");
1390281806Srpaulo		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
1391189251Ssam		wps->state = WPS_FINISHED;
1392189251Ssam		return WPS_DONE;
1393189251Ssam	}
1394189251Ssam
1395189251Ssam	return WPS_FAILURE;
1396189251Ssam}
1397189251Ssam
1398189251Ssam
1399189251Ssamstatic enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
1400189251Ssam						 const struct wpabuf *msg)
1401189251Ssam{
1402189251Ssam	struct wps_parse_attr attr;
1403252726Srpaulo	u16 config_error;
1404189251Ssam
1405189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
1406189251Ssam
1407189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1408189251Ssam		return WPS_FAILURE;
1409189251Ssam
1410189251Ssam	if (attr.msg_type == NULL) {
1411189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1412189251Ssam		return WPS_FAILURE;
1413189251Ssam	}
1414189251Ssam
1415189251Ssam	if (*attr.msg_type != WPS_WSC_NACK) {
1416189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1417189251Ssam			   *attr.msg_type);
1418189251Ssam		return WPS_FAILURE;
1419189251Ssam	}
1420189251Ssam
1421189251Ssam	if (attr.registrar_nonce == NULL ||
1422252726Srpaulo	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1423189251Ssam	{
1424189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1425189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
1426189251Ssam			    attr.registrar_nonce, WPS_NONCE_LEN);
1427189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
1428189251Ssam			    wps->nonce_r, WPS_NONCE_LEN);
1429189251Ssam		return WPS_FAILURE;
1430189251Ssam	}
1431189251Ssam
1432189251Ssam	if (attr.enrollee_nonce == NULL ||
1433252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1434189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1435189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
1436189251Ssam			    attr.enrollee_nonce, WPS_NONCE_LEN);
1437189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
1438189251Ssam			    wps->nonce_e, WPS_NONCE_LEN);
1439189251Ssam		return WPS_FAILURE;
1440189251Ssam	}
1441189251Ssam
1442189251Ssam	if (attr.config_error == NULL) {
1443189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
1444189251Ssam			   "in WSC_NACK");
1445189251Ssam		return WPS_FAILURE;
1446189251Ssam	}
1447189251Ssam
1448252726Srpaulo	config_error = WPA_GET_BE16(attr.config_error);
1449189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1450252726Srpaulo		   "Configuration Error %d", config_error);
1451189251Ssam
1452189251Ssam	switch (wps->state) {
1453189251Ssam	case RECV_M4:
1454252726Srpaulo		wps_fail_event(wps->wps, WPS_M3, config_error,
1455281806Srpaulo			       wps->error_indication, wps->peer_dev.mac_addr);
1456189251Ssam		break;
1457189251Ssam	case RECV_M6:
1458252726Srpaulo		wps_fail_event(wps->wps, WPS_M5, config_error,
1459281806Srpaulo			       wps->error_indication, wps->peer_dev.mac_addr);
1460189251Ssam		break;
1461189251Ssam	case RECV_M8:
1462252726Srpaulo		wps_fail_event(wps->wps, WPS_M7, config_error,
1463281806Srpaulo			       wps->error_indication, wps->peer_dev.mac_addr);
1464189251Ssam		break;
1465189251Ssam	default:
1466189251Ssam		break;
1467189251Ssam	}
1468189251Ssam
1469189251Ssam	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
1470189251Ssam	 * Enrollee is Authenticator */
1471189251Ssam	wps->state = SEND_WSC_NACK;
1472189251Ssam
1473189251Ssam	return WPS_FAILURE;
1474189251Ssam}
1475189251Ssam
1476189251Ssam
1477189251Ssamenum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
1478189251Ssam					      enum wsc_op_code op_code,
1479189251Ssam					      const struct wpabuf *msg)
1480189251Ssam{
1481189251Ssam
1482189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
1483189251Ssam		   "op_code=%d)",
1484189251Ssam		   (unsigned long) wpabuf_len(msg), op_code);
1485189251Ssam
1486209158Srpaulo	if (op_code == WSC_UPnP) {
1487209158Srpaulo		/* Determine the OpCode based on message type attribute */
1488209158Srpaulo		struct wps_parse_attr attr;
1489209158Srpaulo		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
1490209158Srpaulo			if (*attr.msg_type == WPS_WSC_ACK)
1491209158Srpaulo				op_code = WSC_ACK;
1492209158Srpaulo			else if (*attr.msg_type == WPS_WSC_NACK)
1493209158Srpaulo				op_code = WSC_NACK;
1494209158Srpaulo		}
1495209158Srpaulo	}
1496209158Srpaulo
1497189251Ssam	switch (op_code) {
1498189251Ssam	case WSC_MSG:
1499189251Ssam	case WSC_UPnP:
1500189251Ssam		return wps_process_wsc_msg(wps, msg);
1501189251Ssam	case WSC_ACK:
1502252726Srpaulo		if (wps_validate_wsc_ack(msg) < 0)
1503252726Srpaulo			return WPS_FAILURE;
1504189251Ssam		return wps_process_wsc_ack(wps, msg);
1505189251Ssam	case WSC_NACK:
1506252726Srpaulo		if (wps_validate_wsc_nack(msg) < 0)
1507252726Srpaulo			return WPS_FAILURE;
1508189251Ssam		return wps_process_wsc_nack(wps, msg);
1509189251Ssam	default:
1510189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
1511189251Ssam		return WPS_FAILURE;
1512189251Ssam	}
1513189251Ssam}
1514