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;
108189251Ssam
109252726Srpaulo	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
110189251Ssam		return NULL;
111189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
112189251Ssam		    wps->nonce_e, WPS_NONCE_LEN);
113189251Ssam
114189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
115189251Ssam	msg = wpabuf_alloc(1000);
116189251Ssam	if (msg == NULL)
117189251Ssam		return NULL;
118189251Ssam
119252726Srpaulo	config_methods = wps->wps->config_methods;
120252726Srpaulo	if (wps->wps->ap && !wps->pbc_in_m1 &&
121252726Srpaulo	    (wps->dev_password_len != 0 ||
122252726Srpaulo	     (config_methods & WPS_CONFIG_DISPLAY))) {
123252726Srpaulo		/*
124252726Srpaulo		 * These are the methods that the AP supports as an Enrollee
125252726Srpaulo		 * for adding external Registrars, so remove PushButton.
126252726Srpaulo		 *
127252726Srpaulo		 * As a workaround for Windows 7 mechanism for probing WPS
128252726Srpaulo		 * capabilities from M1, leave PushButton option if no PIN
129252726Srpaulo		 * method is available or if WPS configuration enables PBC
130252726Srpaulo		 * workaround.
131252726Srpaulo		 */
132252726Srpaulo		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
133252726Srpaulo		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
134252726Srpaulo				    WPS_CONFIG_PHY_PUSHBUTTON);
135252726Srpaulo	}
136252726Srpaulo
137189251Ssam	if (wps_build_version(msg) ||
138189251Ssam	    wps_build_msg_type(msg, WPS_M1) ||
139189251Ssam	    wps_build_uuid_e(msg, wps->uuid_e) ||
140281806Srpaulo	    wps_build_mac_addr(msg, wps->mac_addr_e) ||
141189251Ssam	    wps_build_enrollee_nonce(wps, msg) ||
142189251Ssam	    wps_build_public_key(wps, msg) ||
143189251Ssam	    wps_build_auth_type_flags(wps, msg) ||
144189251Ssam	    wps_build_encr_type_flags(wps, msg) ||
145189251Ssam	    wps_build_conn_type_flags(wps, msg) ||
146252726Srpaulo	    wps_build_config_methods(msg, config_methods) ||
147189251Ssam	    wps_build_wps_state(wps, msg) ||
148189251Ssam	    wps_build_device_attrs(&wps->wps->dev, msg) ||
149281806Srpaulo	    wps_build_rf_bands(&wps->wps->dev, msg,
150281806Srpaulo			       wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
151189251Ssam	    wps_build_assoc_state(wps, msg) ||
152189251Ssam	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
153189251Ssam	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
154252726Srpaulo	    wps_build_os_version(&wps->wps->dev, msg) ||
155252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
156252726Srpaulo	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
157189251Ssam		wpabuf_free(msg);
158189251Ssam		return NULL;
159189251Ssam	}
160189251Ssam
161189251Ssam	wps->state = RECV_M2;
162189251Ssam	return msg;
163189251Ssam}
164189251Ssam
165189251Ssam
166189251Ssamstatic struct wpabuf * wps_build_m3(struct wps_data *wps)
167189251Ssam{
168189251Ssam	struct wpabuf *msg;
169189251Ssam
170189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
171189251Ssam
172189251Ssam	if (wps->dev_password == NULL) {
173189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
174189251Ssam		return NULL;
175189251Ssam	}
176189251Ssam	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
177189251Ssam
178281806Srpaulo	if (wps->wps->ap && random_pool_ready() != 1) {
179281806Srpaulo		wpa_printf(MSG_INFO,
180281806Srpaulo			   "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
181281806Srpaulo		return NULL;
182281806Srpaulo	}
183281806Srpaulo
184189251Ssam	msg = wpabuf_alloc(1000);
185189251Ssam	if (msg == NULL)
186189251Ssam		return NULL;
187189251Ssam
188189251Ssam	if (wps_build_version(msg) ||
189189251Ssam	    wps_build_msg_type(msg, WPS_M3) ||
190189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
191189251Ssam	    wps_build_e_hash(wps, msg) ||
192252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
193189251Ssam	    wps_build_authenticator(wps, msg)) {
194189251Ssam		wpabuf_free(msg);
195189251Ssam		return NULL;
196189251Ssam	}
197189251Ssam
198189251Ssam	wps->state = RECV_M4;
199189251Ssam	return msg;
200189251Ssam}
201189251Ssam
202189251Ssam
203189251Ssamstatic struct wpabuf * wps_build_m5(struct wps_data *wps)
204189251Ssam{
205189251Ssam	struct wpabuf *msg, *plain;
206189251Ssam
207189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
208189251Ssam
209189251Ssam	plain = wpabuf_alloc(200);
210189251Ssam	if (plain == NULL)
211189251Ssam		return NULL;
212189251Ssam
213189251Ssam	msg = wpabuf_alloc(1000);
214189251Ssam	if (msg == NULL) {
215189251Ssam		wpabuf_free(plain);
216189251Ssam		return NULL;
217189251Ssam	}
218189251Ssam
219189251Ssam	if (wps_build_version(msg) ||
220189251Ssam	    wps_build_msg_type(msg, WPS_M5) ||
221189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
222189251Ssam	    wps_build_e_snonce1(wps, plain) ||
223189251Ssam	    wps_build_key_wrap_auth(wps, plain) ||
224189251Ssam	    wps_build_encr_settings(wps, msg, plain) ||
225252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
226189251Ssam	    wps_build_authenticator(wps, msg)) {
227189251Ssam		wpabuf_free(plain);
228189251Ssam		wpabuf_free(msg);
229189251Ssam		return NULL;
230189251Ssam	}
231189251Ssam	wpabuf_free(plain);
232189251Ssam
233189251Ssam	wps->state = RECV_M6;
234189251Ssam	return msg;
235189251Ssam}
236189251Ssam
237189251Ssam
238189251Ssamstatic int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
239189251Ssam{
240189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
241189251Ssam	wpabuf_put_be16(msg, ATTR_SSID);
242189251Ssam	wpabuf_put_be16(msg, wps->wps->ssid_len);
243189251Ssam	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
244189251Ssam	return 0;
245189251Ssam}
246189251Ssam
247189251Ssam
248189251Ssamstatic int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
249189251Ssam{
250281806Srpaulo	u16 auth_type = wps->wps->ap_auth_type;
251252726Srpaulo
252281806Srpaulo	/*
253281806Srpaulo	 * Work around issues with Windows 7 WPS implementation not liking
254281806Srpaulo	 * multiple Authentication Type bits in M7 AP Settings attribute by
255281806Srpaulo	 * showing only the most secure option from current configuration.
256281806Srpaulo	 */
257252726Srpaulo	if (auth_type & WPS_AUTH_WPA2PSK)
258252726Srpaulo		auth_type = WPS_AUTH_WPA2PSK;
259252726Srpaulo	else if (auth_type & WPS_AUTH_WPAPSK)
260252726Srpaulo		auth_type = WPS_AUTH_WPAPSK;
261252726Srpaulo	else if (auth_type & WPS_AUTH_OPEN)
262252726Srpaulo		auth_type = WPS_AUTH_OPEN;
263252726Srpaulo
264252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
265189251Ssam	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
266189251Ssam	wpabuf_put_be16(msg, 2);
267252726Srpaulo	wpabuf_put_be16(msg, auth_type);
268189251Ssam	return 0;
269189251Ssam}
270189251Ssam
271189251Ssam
272189251Ssamstatic int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
273189251Ssam{
274281806Srpaulo	u16 encr_type = wps->wps->ap_encr_type;
275252726Srpaulo
276281806Srpaulo	/*
277281806Srpaulo	 * Work around issues with Windows 7 WPS implementation not liking
278281806Srpaulo	 * multiple Encryption Type bits in M7 AP Settings attribute by
279281806Srpaulo	 * showing only the most secure option from current configuration.
280281806Srpaulo	 */
281281806Srpaulo	if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
282252726Srpaulo		if (encr_type & WPS_ENCR_AES)
283252726Srpaulo			encr_type = WPS_ENCR_AES;
284252726Srpaulo		else if (encr_type & WPS_ENCR_TKIP)
285252726Srpaulo			encr_type = WPS_ENCR_TKIP;
286252726Srpaulo	}
287252726Srpaulo
288252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
289189251Ssam	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
290189251Ssam	wpabuf_put_be16(msg, 2);
291252726Srpaulo	wpabuf_put_be16(msg, encr_type);
292189251Ssam	return 0;
293189251Ssam}
294189251Ssam
295189251Ssam
296189251Ssamstatic int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
297189251Ssam{
298281806Srpaulo	if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
299281806Srpaulo	    wps->wps->network_key_len == 0) {
300281806Srpaulo		char hex[65];
301281806Srpaulo		u8 psk[32];
302281806Srpaulo		/* Generate a random per-device PSK */
303281806Srpaulo		if (random_pool_ready() != 1 ||
304281806Srpaulo		    random_get_bytes(psk, sizeof(psk)) < 0) {
305281806Srpaulo			wpa_printf(MSG_INFO,
306281806Srpaulo				   "WPS: Could not generate random PSK");
307281806Srpaulo			return -1;
308281806Srpaulo		}
309281806Srpaulo		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
310281806Srpaulo				psk, sizeof(psk));
311281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
312281806Srpaulo			   (unsigned int) wps->new_psk_len * 2);
313281806Srpaulo		wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
314281806Srpaulo		wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
315281806Srpaulo		wpabuf_put_be16(msg, sizeof(psk) * 2);
316281806Srpaulo		wpabuf_put_data(msg, hex, sizeof(psk) * 2);
317281806Srpaulo		if (wps->wps->registrar) {
318281806Srpaulo			wps_cb_new_psk(wps->wps->registrar,
319281806Srpaulo				       wps->peer_dev.mac_addr,
320281806Srpaulo				       wps->p2p_dev_addr, psk, sizeof(psk));
321281806Srpaulo		}
322281806Srpaulo		return 0;
323281806Srpaulo	}
324281806Srpaulo
325281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
326281806Srpaulo		   (unsigned int) wps->wps->network_key_len);
327189251Ssam	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
328189251Ssam	wpabuf_put_be16(msg, wps->wps->network_key_len);
329189251Ssam	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
330189251Ssam	return 0;
331189251Ssam}
332189251Ssam
333189251Ssam
334189251Ssamstatic int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
335189251Ssam{
336189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
337189251Ssam	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
338189251Ssam	wpabuf_put_be16(msg, ETH_ALEN);
339189251Ssam	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
340189251Ssam	return 0;
341189251Ssam}
342189251Ssam
343189251Ssam
344189251Ssamstatic int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
345189251Ssam{
346281806Srpaulo	const u8 *start, *end;
347281806Srpaulo	int ret;
348281806Srpaulo
349189251Ssam	if (wps->wps->ap_settings) {
350189251Ssam		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
351189251Ssam		wpabuf_put_data(plain, wps->wps->ap_settings,
352189251Ssam				wps->wps->ap_settings_len);
353189251Ssam		return 0;
354189251Ssam	}
355189251Ssam
356281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * AP Settings based on current configuration");
357281806Srpaulo	start = wpabuf_put(plain, 0);
358281806Srpaulo	ret = wps_build_cred_ssid(wps, plain) ||
359189251Ssam		wps_build_cred_mac_addr(wps, plain) ||
360189251Ssam		wps_build_cred_auth_type(wps, plain) ||
361189251Ssam		wps_build_cred_encr_type(wps, plain) ||
362189251Ssam		wps_build_cred_network_key(wps, plain);
363281806Srpaulo	end = wpabuf_put(plain, 0);
364281806Srpaulo
365281806Srpaulo	wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
366281806Srpaulo			start, end - start);
367281806Srpaulo
368281806Srpaulo	return ret;
369189251Ssam}
370189251Ssam
371189251Ssam
372189251Ssamstatic struct wpabuf * wps_build_m7(struct wps_data *wps)
373189251Ssam{
374189251Ssam	struct wpabuf *msg, *plain;
375189251Ssam
376189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
377189251Ssam
378189251Ssam	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
379189251Ssam	if (plain == NULL)
380189251Ssam		return NULL;
381189251Ssam
382189251Ssam	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
383189251Ssam	if (msg == NULL) {
384189251Ssam		wpabuf_free(plain);
385189251Ssam		return NULL;
386189251Ssam	}
387189251Ssam
388189251Ssam	if (wps_build_version(msg) ||
389189251Ssam	    wps_build_msg_type(msg, WPS_M7) ||
390189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
391189251Ssam	    wps_build_e_snonce2(wps, plain) ||
392189251Ssam	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
393189251Ssam	    wps_build_key_wrap_auth(wps, plain) ||
394189251Ssam	    wps_build_encr_settings(wps, msg, plain) ||
395252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
396189251Ssam	    wps_build_authenticator(wps, msg)) {
397189251Ssam		wpabuf_free(plain);
398189251Ssam		wpabuf_free(msg);
399189251Ssam		return NULL;
400189251Ssam	}
401189251Ssam	wpabuf_free(plain);
402189251Ssam
403214734Srpaulo	if (wps->wps->ap && wps->wps->registrar) {
404214734Srpaulo		/*
405214734Srpaulo		 * If the Registrar is only learning our current configuration,
406214734Srpaulo		 * it may not continue protocol run to successful completion.
407214734Srpaulo		 * Store information here to make sure it remains available.
408214734Srpaulo		 */
409214734Srpaulo		wps_device_store(wps->wps->registrar, &wps->peer_dev,
410214734Srpaulo				 wps->uuid_r);
411214734Srpaulo	}
412214734Srpaulo
413189251Ssam	wps->state = RECV_M8;
414189251Ssam	return msg;
415189251Ssam}
416189251Ssam
417189251Ssam
418189251Ssamstatic struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
419189251Ssam{
420189251Ssam	struct wpabuf *msg;
421189251Ssam
422189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
423189251Ssam
424189251Ssam	msg = wpabuf_alloc(1000);
425189251Ssam	if (msg == NULL)
426189251Ssam		return NULL;
427189251Ssam
428189251Ssam	if (wps_build_version(msg) ||
429189251Ssam	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
430189251Ssam	    wps_build_enrollee_nonce(wps, msg) ||
431252726Srpaulo	    wps_build_registrar_nonce(wps, msg) ||
432252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
433189251Ssam		wpabuf_free(msg);
434189251Ssam		return NULL;
435189251Ssam	}
436189251Ssam
437189251Ssam	if (wps->wps->ap)
438189251Ssam		wps->state = RECV_ACK;
439189251Ssam	else {
440281806Srpaulo		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
441189251Ssam		wps->state = WPS_FINISHED;
442189251Ssam	}
443189251Ssam	return msg;
444189251Ssam}
445189251Ssam
446189251Ssam
447189251Ssamstruct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
448189251Ssam				     enum wsc_op_code *op_code)
449189251Ssam{
450189251Ssam	struct wpabuf *msg;
451189251Ssam
452189251Ssam	switch (wps->state) {
453189251Ssam	case SEND_M1:
454189251Ssam		msg = wps_build_m1(wps);
455189251Ssam		*op_code = WSC_MSG;
456189251Ssam		break;
457189251Ssam	case SEND_M3:
458189251Ssam		msg = wps_build_m3(wps);
459189251Ssam		*op_code = WSC_MSG;
460189251Ssam		break;
461189251Ssam	case SEND_M5:
462189251Ssam		msg = wps_build_m5(wps);
463189251Ssam		*op_code = WSC_MSG;
464189251Ssam		break;
465189251Ssam	case SEND_M7:
466189251Ssam		msg = wps_build_m7(wps);
467189251Ssam		*op_code = WSC_MSG;
468189251Ssam		break;
469189251Ssam	case RECEIVED_M2D:
470189251Ssam		if (wps->wps->ap) {
471189251Ssam			msg = wps_build_wsc_nack(wps);
472189251Ssam			*op_code = WSC_NACK;
473189251Ssam			break;
474189251Ssam		}
475189251Ssam		msg = wps_build_wsc_ack(wps);
476189251Ssam		*op_code = WSC_ACK;
477189251Ssam		if (msg) {
478189251Ssam			/* Another M2/M2D may be received */
479189251Ssam			wps->state = RECV_M2;
480189251Ssam		}
481189251Ssam		break;
482189251Ssam	case SEND_WSC_NACK:
483189251Ssam		msg = wps_build_wsc_nack(wps);
484189251Ssam		*op_code = WSC_NACK;
485189251Ssam		break;
486189251Ssam	case WPS_MSG_DONE:
487189251Ssam		msg = wps_build_wsc_done(wps);
488189251Ssam		*op_code = WSC_Done;
489189251Ssam		break;
490189251Ssam	default:
491189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
492189251Ssam			   "a message", wps->state);
493189251Ssam		msg = NULL;
494189251Ssam		break;
495189251Ssam	}
496189251Ssam
497189251Ssam	if (*op_code == WSC_MSG && msg) {
498189251Ssam		/* Save a copy of the last message for Authenticator derivation
499189251Ssam		 */
500189251Ssam		wpabuf_free(wps->last_msg);
501189251Ssam		wps->last_msg = wpabuf_dup(msg);
502189251Ssam	}
503189251Ssam
504189251Ssam	return msg;
505189251Ssam}
506189251Ssam
507189251Ssam
508189251Ssamstatic int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
509189251Ssam{
510189251Ssam	if (r_nonce == NULL) {
511189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
512189251Ssam		return -1;
513189251Ssam	}
514189251Ssam
515189251Ssam	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
516189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
517189251Ssam		    wps->nonce_r, WPS_NONCE_LEN);
518189251Ssam
519189251Ssam	return 0;
520189251Ssam}
521189251Ssam
522189251Ssam
523189251Ssamstatic int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
524189251Ssam{
525189251Ssam	if (e_nonce == NULL) {
526189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
527189251Ssam		return -1;
528189251Ssam	}
529189251Ssam
530189251Ssam	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
531189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
532189251Ssam		return -1;
533189251Ssam	}
534189251Ssam
535189251Ssam	return 0;
536189251Ssam}
537189251Ssam
538189251Ssam
539189251Ssamstatic int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
540189251Ssam{
541189251Ssam	if (uuid_r == NULL) {
542189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
543189251Ssam		return -1;
544189251Ssam	}
545189251Ssam
546189251Ssam	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
547189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
548189251Ssam
549189251Ssam	return 0;
550189251Ssam}
551189251Ssam
552189251Ssam
553189251Ssamstatic int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
554189251Ssam			      size_t pk_len)
555189251Ssam{
556189251Ssam	if (pk == NULL || pk_len == 0) {
557189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
558189251Ssam		return -1;
559189251Ssam	}
560189251Ssam
561281806Srpaulo	if (wps->peer_pubkey_hash_set) {
562281806Srpaulo		u8 hash[WPS_HASH_LEN];
563281806Srpaulo		sha256_vector(1, &pk, &pk_len, hash);
564281806Srpaulo		if (os_memcmp_const(hash, wps->peer_pubkey_hash,
565281806Srpaulo				    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
566281806Srpaulo			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
567281806Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
568281806Srpaulo				    pk, pk_len);
569281806Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
570281806Srpaulo				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
571281806Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
572281806Srpaulo				    wps->peer_pubkey_hash,
573281806Srpaulo				    WPS_OOB_PUBKEY_HASH_LEN);
574281806Srpaulo			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
575281806Srpaulo			return -1;
576281806Srpaulo		}
577281806Srpaulo	}
578281806Srpaulo
579189251Ssam	wpabuf_free(wps->dh_pubkey_r);
580189251Ssam	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
581189251Ssam	if (wps->dh_pubkey_r == NULL)
582189251Ssam		return -1;
583189251Ssam
584189251Ssam	if (wps_derive_keys(wps) < 0)
585189251Ssam		return -1;
586189251Ssam
587189251Ssam	return 0;
588189251Ssam}
589189251Ssam
590189251Ssam
591189251Ssamstatic int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
592189251Ssam{
593189251Ssam	if (r_hash1 == NULL) {
594189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
595189251Ssam		return -1;
596189251Ssam	}
597189251Ssam
598189251Ssam	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
599189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
600189251Ssam
601189251Ssam	return 0;
602189251Ssam}
603189251Ssam
604189251Ssam
605189251Ssamstatic int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
606189251Ssam{
607189251Ssam	if (r_hash2 == NULL) {
608189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
609189251Ssam		return -1;
610189251Ssam	}
611189251Ssam
612189251Ssam	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
613189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
614189251Ssam
615189251Ssam	return 0;
616189251Ssam}
617189251Ssam
618189251Ssam
619189251Ssamstatic int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
620189251Ssam{
621189251Ssam	u8 hash[SHA256_MAC_LEN];
622189251Ssam	const u8 *addr[4];
623189251Ssam	size_t len[4];
624189251Ssam
625189251Ssam	if (r_snonce1 == NULL) {
626189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
627189251Ssam		return -1;
628189251Ssam	}
629189251Ssam
630189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
631189251Ssam			WPS_SECRET_NONCE_LEN);
632189251Ssam
633189251Ssam	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
634189251Ssam	addr[0] = r_snonce1;
635189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
636189251Ssam	addr[1] = wps->psk1;
637189251Ssam	len[1] = WPS_PSK_LEN;
638189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
639189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
640189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
641189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
642189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
643189251Ssam
644281806Srpaulo	if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
645189251Ssam		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
646189251Ssam			   "not match with the pre-committed value");
647189251Ssam		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
648281806Srpaulo		wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr);
649189251Ssam		return -1;
650189251Ssam	}
651189251Ssam
652189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
653189251Ssam		   "half of the device password");
654189251Ssam
655189251Ssam	return 0;
656189251Ssam}
657189251Ssam
658189251Ssam
659189251Ssamstatic int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
660189251Ssam{
661189251Ssam	u8 hash[SHA256_MAC_LEN];
662189251Ssam	const u8 *addr[4];
663189251Ssam	size_t len[4];
664189251Ssam
665189251Ssam	if (r_snonce2 == NULL) {
666189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
667189251Ssam		return -1;
668189251Ssam	}
669189251Ssam
670189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
671189251Ssam			WPS_SECRET_NONCE_LEN);
672189251Ssam
673189251Ssam	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
674189251Ssam	addr[0] = r_snonce2;
675189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
676189251Ssam	addr[1] = wps->psk2;
677189251Ssam	len[1] = WPS_PSK_LEN;
678189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
679189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
680189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
681189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
682189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
683189251Ssam
684281806Srpaulo	if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
685189251Ssam		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
686189251Ssam			   "not match with the pre-committed value");
687189251Ssam		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
688281806Srpaulo		wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr);
689189251Ssam		return -1;
690189251Ssam	}
691189251Ssam
692189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
693189251Ssam		   "half of the device password");
694189251Ssam
695189251Ssam	return 0;
696189251Ssam}
697189251Ssam
698189251Ssam
699189251Ssamstatic int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
700252726Srpaulo			      size_t cred_len, int wps2)
701189251Ssam{
702189251Ssam	struct wps_parse_attr attr;
703189251Ssam	struct wpabuf msg;
704252726Srpaulo	int ret = 0;
705189251Ssam
706189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
707189251Ssam	os_memset(&wps->cred, 0, sizeof(wps->cred));
708189251Ssam	wpabuf_set(&msg, cred, cred_len);
709189251Ssam	if (wps_parse_msg(&msg, &attr) < 0 ||
710189251Ssam	    wps_process_cred(&attr, &wps->cred))
711189251Ssam		return -1;
712189251Ssam
713209158Srpaulo	if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
714209158Srpaulo	    0) {
715209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
716209158Srpaulo			   MACSTR ") does not match with own address (" MACSTR
717209158Srpaulo			   ")", MAC2STR(wps->cred.mac_addr),
718209158Srpaulo			   MAC2STR(wps->wps->dev.mac_addr));
719209158Srpaulo		/*
720209158Srpaulo		 * In theory, this could be consider fatal error, but there are
721209158Srpaulo		 * number of deployed implementations using other address here
722209158Srpaulo		 * due to unclarity in the specification. For interoperability
723209158Srpaulo		 * reasons, allow this to be processed since we do not really
724209158Srpaulo		 * use the MAC Address information for anything.
725209158Srpaulo		 */
726252726Srpaulo#ifdef CONFIG_WPS_STRICT
727252726Srpaulo		if (wps2) {
728252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
729252726Srpaulo				   "MAC Address in AP Settings");
730252726Srpaulo			return -1;
731252726Srpaulo		}
732252726Srpaulo#endif /* CONFIG_WPS_STRICT */
733209158Srpaulo	}
734209158Srpaulo
735252726Srpaulo	if (!(wps->cred.encr_type &
736252726Srpaulo	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
737252726Srpaulo		if (wps->cred.encr_type & WPS_ENCR_WEP) {
738252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Reject Credential "
739252726Srpaulo				   "due to WEP configuration");
740252726Srpaulo			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
741252726Srpaulo			return -2;
742252726Srpaulo		}
743252726Srpaulo
744252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
745252726Srpaulo			   "invalid encr_type 0x%x", wps->cred.encr_type);
746252726Srpaulo		return -1;
747252726Srpaulo	}
748252726Srpaulo
749189251Ssam	if (wps->wps->cred_cb) {
750189251Ssam		wps->cred.cred_attr = cred - 4;
751189251Ssam		wps->cred.cred_attr_len = cred_len + 4;
752252726Srpaulo		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
753189251Ssam		wps->cred.cred_attr = NULL;
754189251Ssam		wps->cred.cred_attr_len = 0;
755189251Ssam	}
756189251Ssam
757252726Srpaulo	return ret;
758189251Ssam}
759189251Ssam
760189251Ssam
761189251Ssamstatic int wps_process_creds(struct wps_data *wps, const u8 *cred[],
762289549Srpaulo			     u16 cred_len[], unsigned int num_cred, int wps2)
763189251Ssam{
764189251Ssam	size_t i;
765252726Srpaulo	int ok = 0;
766189251Ssam
767189251Ssam	if (wps->wps->ap)
768189251Ssam		return 0;
769189251Ssam
770189251Ssam	if (num_cred == 0) {
771189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
772189251Ssam			   "received");
773189251Ssam		return -1;
774189251Ssam	}
775189251Ssam
776189251Ssam	for (i = 0; i < num_cred; i++) {
777252726Srpaulo		int res;
778252726Srpaulo		res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
779252726Srpaulo		if (res == 0)
780252726Srpaulo			ok++;
781252726Srpaulo		else if (res == -2)
782252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
783252726Srpaulo		else
784189251Ssam			return -1;
785189251Ssam	}
786189251Ssam
787252726Srpaulo	if (ok == 0) {
788252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
789252726Srpaulo			   "received");
790252726Srpaulo		return -1;
791252726Srpaulo	}
792252726Srpaulo
793189251Ssam	return 0;
794189251Ssam}
795189251Ssam
796189251Ssam
797189251Ssamstatic int wps_process_ap_settings_e(struct wps_data *wps,
798189251Ssam				     struct wps_parse_attr *attr,
799252726Srpaulo				     struct wpabuf *attrs, int wps2)
800189251Ssam{
801189251Ssam	struct wps_credential cred;
802289549Srpaulo	int ret = 0;
803189251Ssam
804189251Ssam	if (!wps->wps->ap)
805189251Ssam		return 0;
806189251Ssam
807189251Ssam	if (wps_process_ap_settings(attr, &cred) < 0)
808189251Ssam		return -1;
809189251Ssam
810189251Ssam	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
811189251Ssam		   "Registrar");
812189251Ssam
813209158Srpaulo	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
814209158Srpaulo	    0) {
815209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
816209158Srpaulo			   MACSTR ") does not match with own address (" MACSTR
817209158Srpaulo			   ")", MAC2STR(cred.mac_addr),
818209158Srpaulo			   MAC2STR(wps->wps->dev.mac_addr));
819209158Srpaulo		/*
820209158Srpaulo		 * In theory, this could be consider fatal error, but there are
821209158Srpaulo		 * number of deployed implementations using other address here
822209158Srpaulo		 * due to unclarity in the specification. For interoperability
823209158Srpaulo		 * reasons, allow this to be processed since we do not really
824209158Srpaulo		 * use the MAC Address information for anything.
825209158Srpaulo		 */
826252726Srpaulo#ifdef CONFIG_WPS_STRICT
827252726Srpaulo		if (wps2) {
828252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
829252726Srpaulo				   "MAC Address in AP Settings");
830252726Srpaulo			return -1;
831252726Srpaulo		}
832252726Srpaulo#endif /* CONFIG_WPS_STRICT */
833209158Srpaulo	}
834209158Srpaulo
835252726Srpaulo	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
836252726Srpaulo	{
837252726Srpaulo		if (cred.encr_type & WPS_ENCR_WEP) {
838252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
839252726Srpaulo				   "due to WEP configuration");
840252726Srpaulo			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
841252726Srpaulo			return -1;
842252726Srpaulo		}
843252726Srpaulo
844252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
845252726Srpaulo			   "invalid encr_type 0x%x", cred.encr_type);
846252726Srpaulo		return -1;
847252726Srpaulo	}
848252726Srpaulo
849252726Srpaulo#ifdef CONFIG_WPS_STRICT
850252726Srpaulo	if (wps2) {
851252726Srpaulo		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
852252726Srpaulo		    WPS_ENCR_TKIP ||
853252726Srpaulo		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
854252726Srpaulo		    WPS_AUTH_WPAPSK) {
855252726Srpaulo			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
856252726Srpaulo				   "AP Settings: WPA-Personal/TKIP only");
857252726Srpaulo			wps->error_indication =
858252726Srpaulo				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
859252726Srpaulo			return -1;
860252726Srpaulo		}
861252726Srpaulo	}
862252726Srpaulo#endif /* CONFIG_WPS_STRICT */
863252726Srpaulo
864252726Srpaulo	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
865252726Srpaulo	{
866252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
867252726Srpaulo			   "TKIP+AES");
868252726Srpaulo		cred.encr_type |= WPS_ENCR_AES;
869252726Srpaulo	}
870252726Srpaulo
871252726Srpaulo	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
872252726Srpaulo	    WPS_AUTH_WPAPSK) {
873252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
874252726Srpaulo			   "WPAPSK+WPA2PSK");
875252726Srpaulo		cred.auth_type |= WPS_AUTH_WPA2PSK;
876252726Srpaulo	}
877252726Srpaulo
878189251Ssam	if (wps->wps->cred_cb) {
879189251Ssam		cred.cred_attr = wpabuf_head(attrs);
880189251Ssam		cred.cred_attr_len = wpabuf_len(attrs);
881289549Srpaulo		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
882189251Ssam	}
883189251Ssam
884289549Srpaulo	return ret;
885189251Ssam}
886189251Ssam
887189251Ssam
888281806Srpaulostatic int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
889281806Srpaulo{
890281806Srpaulo	u16 id;
891281806Srpaulo
892281806Srpaulo	if (dev_pw_id == NULL) {
893281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
894281806Srpaulo		return -1;
895281806Srpaulo	}
896281806Srpaulo
897281806Srpaulo	id = WPA_GET_BE16(dev_pw_id);
898281806Srpaulo	if (wps->dev_pw_id == id) {
899281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
900281806Srpaulo		return 0;
901281806Srpaulo	}
902281806Srpaulo
903281806Srpaulo#ifdef CONFIG_P2P
904281806Srpaulo	if ((id == DEV_PW_DEFAULT &&
905281806Srpaulo	     wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
906281806Srpaulo	    (id == DEV_PW_REGISTRAR_SPECIFIED &&
907281806Srpaulo	     wps->dev_pw_id == DEV_PW_DEFAULT)) {
908281806Srpaulo		/*
909281806Srpaulo		 * Common P2P use cases indicate whether the PIN is from the
910281806Srpaulo		 * client or GO using Device Password Id in M1/M2 in a way that
911281806Srpaulo		 * does not look fully compliant with WSC specification. Anyway,
912281806Srpaulo		 * this is deployed and needs to be allowed, so ignore changes
913281806Srpaulo		 * between Registrar-Specified and Default PIN.
914281806Srpaulo		 */
915281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
916281806Srpaulo			   "change");
917281806Srpaulo		return 0;
918281806Srpaulo	}
919281806Srpaulo#endif /* CONFIG_P2P */
920281806Srpaulo
921281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
922281806Srpaulo		   "ID from %u to %u", wps->dev_pw_id, id);
923281806Srpaulo
924281806Srpaulo	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
925281806Srpaulo		wpa_printf(MSG_DEBUG,
926281806Srpaulo			   "WPS: Workaround - ignore PBC-to-PIN change");
927281806Srpaulo		return 0;
928281806Srpaulo	}
929281806Srpaulo
930281806Srpaulo	if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
931281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
932281806Srpaulo		bin_clear_free(wps->dev_password, wps->dev_password_len);
933281806Srpaulo		wps->dev_pw_id = wps->alt_dev_pw_id;
934281806Srpaulo		wps->dev_password = wps->alt_dev_password;
935281806Srpaulo		wps->dev_password_len = wps->alt_dev_password_len;
936281806Srpaulo		wps->alt_dev_password = NULL;
937281806Srpaulo		wps->alt_dev_password_len = 0;
938281806Srpaulo		return 0;
939281806Srpaulo	}
940281806Srpaulo
941281806Srpaulo	return -1;
942281806Srpaulo}
943281806Srpaulo
944281806Srpaulo
945189251Ssamstatic enum wps_process_res wps_process_m2(struct wps_data *wps,
946189251Ssam					   const struct wpabuf *msg,
947189251Ssam					   struct wps_parse_attr *attr)
948189251Ssam{
949189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M2");
950189251Ssam
951189251Ssam	if (wps->state != RECV_M2) {
952189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
953189251Ssam			   "receiving M2", wps->state);
954189251Ssam		wps->state = SEND_WSC_NACK;
955189251Ssam		return WPS_CONTINUE;
956189251Ssam	}
957189251Ssam
958189251Ssam	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
959189251Ssam	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
960281806Srpaulo	    wps_process_uuid_r(wps, attr->uuid_r) ||
961281806Srpaulo	    wps_process_dev_pw_id(wps, attr->dev_password_id)) {
962189251Ssam		wps->state = SEND_WSC_NACK;
963189251Ssam		return WPS_CONTINUE;
964189251Ssam	}
965189251Ssam
966252726Srpaulo	/*
967252726Srpaulo	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
968252726Srpaulo	 * special locked mode is used to allow protocol run up to M7 in order
969252726Srpaulo	 * to support external Registrars that only learn the current AP
970252726Srpaulo	 * configuration without changing it.
971252726Srpaulo	 */
972214734Srpaulo	if (wps->wps->ap &&
973252726Srpaulo	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
974252726Srpaulo	     wps->dev_password == NULL)) {
975189251Ssam		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
976189251Ssam			   "registration of a new Registrar");
977189251Ssam		wps->config_error = WPS_CFG_SETUP_LOCKED;
978189251Ssam		wps->state = SEND_WSC_NACK;
979189251Ssam		return WPS_CONTINUE;
980189251Ssam	}
981189251Ssam
982214734Srpaulo	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
983214734Srpaulo	    wps_process_authenticator(wps, attr->authenticator, msg) ||
984214734Srpaulo	    wps_process_device_attrs(&wps->peer_dev, attr)) {
985214734Srpaulo		wps->state = SEND_WSC_NACK;
986214734Srpaulo		return WPS_CONTINUE;
987214734Srpaulo	}
988214734Srpaulo
989281806Srpaulo#ifdef CONFIG_WPS_NFC
990281806Srpaulo	if (wps->peer_pubkey_hash_set) {
991281806Srpaulo		struct wpabuf *decrypted;
992281806Srpaulo		struct wps_parse_attr eattr;
993281806Srpaulo
994281806Srpaulo		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
995281806Srpaulo						      attr->encr_settings_len);
996281806Srpaulo		if (decrypted == NULL) {
997281806Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
998281806Srpaulo				   "Encrypted Settings attribute");
999281806Srpaulo			wps->state = SEND_WSC_NACK;
1000281806Srpaulo			return WPS_CONTINUE;
1001281806Srpaulo		}
1002281806Srpaulo
1003281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
1004281806Srpaulo			   "Settings attribute");
1005281806Srpaulo		if (wps_parse_msg(decrypted, &eattr) < 0 ||
1006281806Srpaulo		    wps_process_key_wrap_auth(wps, decrypted,
1007281806Srpaulo					      eattr.key_wrap_auth) ||
1008281806Srpaulo		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1009281806Srpaulo				      eattr.num_cred, attr->version2 != NULL)) {
1010281806Srpaulo			wpabuf_free(decrypted);
1011281806Srpaulo			wps->state = SEND_WSC_NACK;
1012281806Srpaulo			return WPS_CONTINUE;
1013281806Srpaulo		}
1014281806Srpaulo		wpabuf_free(decrypted);
1015281806Srpaulo
1016281806Srpaulo		wps->state = WPS_MSG_DONE;
1017281806Srpaulo		return WPS_CONTINUE;
1018281806Srpaulo	}
1019281806Srpaulo#endif /* CONFIG_WPS_NFC */
1020281806Srpaulo
1021189251Ssam	wps->state = SEND_M3;
1022189251Ssam	return WPS_CONTINUE;
1023189251Ssam}
1024189251Ssam
1025189251Ssam
1026189251Ssamstatic enum wps_process_res wps_process_m2d(struct wps_data *wps,
1027189251Ssam					    struct wps_parse_attr *attr)
1028189251Ssam{
1029189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
1030189251Ssam
1031189251Ssam	if (wps->state != RECV_M2) {
1032189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1033189251Ssam			   "receiving M2D", wps->state);
1034189251Ssam		wps->state = SEND_WSC_NACK;
1035189251Ssam		return WPS_CONTINUE;
1036189251Ssam	}
1037189251Ssam
1038189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
1039189251Ssam			  attr->manufacturer, attr->manufacturer_len);
1040189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
1041189251Ssam			  attr->model_name, attr->model_name_len);
1042189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
1043189251Ssam			  attr->model_number, attr->model_number_len);
1044189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
1045189251Ssam			  attr->serial_number, attr->serial_number_len);
1046189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
1047189251Ssam			  attr->dev_name, attr->dev_name_len);
1048189251Ssam
1049189251Ssam	if (wps->wps->event_cb) {
1050189251Ssam		union wps_event_data data;
1051189251Ssam		struct wps_event_m2d *m2d = &data.m2d;
1052189251Ssam		os_memset(&data, 0, sizeof(data));
1053189251Ssam		if (attr->config_methods)
1054189251Ssam			m2d->config_methods =
1055189251Ssam				WPA_GET_BE16(attr->config_methods);
1056189251Ssam		m2d->manufacturer = attr->manufacturer;
1057189251Ssam		m2d->manufacturer_len = attr->manufacturer_len;
1058189251Ssam		m2d->model_name = attr->model_name;
1059189251Ssam		m2d->model_name_len = attr->model_name_len;
1060189251Ssam		m2d->model_number = attr->model_number;
1061189251Ssam		m2d->model_number_len = attr->model_number_len;
1062189251Ssam		m2d->serial_number = attr->serial_number;
1063189251Ssam		m2d->serial_number_len = attr->serial_number_len;
1064189251Ssam		m2d->dev_name = attr->dev_name;
1065189251Ssam		m2d->dev_name_len = attr->dev_name_len;
1066189251Ssam		m2d->primary_dev_type = attr->primary_dev_type;
1067189251Ssam		if (attr->config_error)
1068189251Ssam			m2d->config_error =
1069189251Ssam				WPA_GET_BE16(attr->config_error);
1070189251Ssam		if (attr->dev_password_id)
1071189251Ssam			m2d->dev_password_id =
1072189251Ssam				WPA_GET_BE16(attr->dev_password_id);
1073189251Ssam		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
1074189251Ssam	}
1075189251Ssam
1076189251Ssam	wps->state = RECEIVED_M2D;
1077189251Ssam	return WPS_CONTINUE;
1078189251Ssam}
1079189251Ssam
1080189251Ssam
1081189251Ssamstatic enum wps_process_res wps_process_m4(struct wps_data *wps,
1082189251Ssam					   const struct wpabuf *msg,
1083189251Ssam					   struct wps_parse_attr *attr)
1084189251Ssam{
1085189251Ssam	struct wpabuf *decrypted;
1086189251Ssam	struct wps_parse_attr eattr;
1087189251Ssam
1088189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M4");
1089189251Ssam
1090189251Ssam	if (wps->state != RECV_M4) {
1091189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1092189251Ssam			   "receiving M4", wps->state);
1093189251Ssam		wps->state = SEND_WSC_NACK;
1094189251Ssam		return WPS_CONTINUE;
1095189251Ssam	}
1096189251Ssam
1097189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1098189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg) ||
1099189251Ssam	    wps_process_r_hash1(wps, attr->r_hash1) ||
1100189251Ssam	    wps_process_r_hash2(wps, attr->r_hash2)) {
1101189251Ssam		wps->state = SEND_WSC_NACK;
1102189251Ssam		return WPS_CONTINUE;
1103189251Ssam	}
1104189251Ssam
1105189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1106189251Ssam					      attr->encr_settings_len);
1107189251Ssam	if (decrypted == NULL) {
1108189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1109189251Ssam			   "Settings attribute");
1110189251Ssam		wps->state = SEND_WSC_NACK;
1111189251Ssam		return WPS_CONTINUE;
1112189251Ssam	}
1113189251Ssam
1114252726Srpaulo	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
1115252726Srpaulo		wpabuf_free(decrypted);
1116252726Srpaulo		wps->state = SEND_WSC_NACK;
1117252726Srpaulo		return WPS_CONTINUE;
1118252726Srpaulo	}
1119252726Srpaulo
1120189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1121189251Ssam		   "attribute");
1122189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1123189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1124189251Ssam	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
1125189251Ssam		wpabuf_free(decrypted);
1126189251Ssam		wps->state = SEND_WSC_NACK;
1127189251Ssam		return WPS_CONTINUE;
1128189251Ssam	}
1129189251Ssam	wpabuf_free(decrypted);
1130189251Ssam
1131189251Ssam	wps->state = SEND_M5;
1132189251Ssam	return WPS_CONTINUE;
1133189251Ssam}
1134189251Ssam
1135189251Ssam
1136189251Ssamstatic enum wps_process_res wps_process_m6(struct wps_data *wps,
1137189251Ssam					   const struct wpabuf *msg,
1138189251Ssam					   struct wps_parse_attr *attr)
1139189251Ssam{
1140189251Ssam	struct wpabuf *decrypted;
1141189251Ssam	struct wps_parse_attr eattr;
1142189251Ssam
1143189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M6");
1144189251Ssam
1145189251Ssam	if (wps->state != RECV_M6) {
1146189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1147189251Ssam			   "receiving M6", wps->state);
1148189251Ssam		wps->state = SEND_WSC_NACK;
1149189251Ssam		return WPS_CONTINUE;
1150189251Ssam	}
1151189251Ssam
1152189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1153189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg)) {
1154189251Ssam		wps->state = SEND_WSC_NACK;
1155189251Ssam		return WPS_CONTINUE;
1156189251Ssam	}
1157189251Ssam
1158189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1159189251Ssam					      attr->encr_settings_len);
1160189251Ssam	if (decrypted == NULL) {
1161189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1162189251Ssam			   "Settings attribute");
1163189251Ssam		wps->state = SEND_WSC_NACK;
1164189251Ssam		return WPS_CONTINUE;
1165189251Ssam	}
1166189251Ssam
1167252726Srpaulo	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1168252726Srpaulo		wpabuf_free(decrypted);
1169252726Srpaulo		wps->state = SEND_WSC_NACK;
1170252726Srpaulo		return WPS_CONTINUE;
1171252726Srpaulo	}
1172252726Srpaulo
1173189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1174189251Ssam		   "attribute");
1175189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1176189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1177189251Ssam	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
1178189251Ssam		wpabuf_free(decrypted);
1179189251Ssam		wps->state = SEND_WSC_NACK;
1180189251Ssam		return WPS_CONTINUE;
1181189251Ssam	}
1182189251Ssam	wpabuf_free(decrypted);
1183189251Ssam
1184252726Srpaulo	if (wps->wps->ap)
1185252726Srpaulo		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1186252726Srpaulo				   NULL);
1187252726Srpaulo
1188189251Ssam	wps->state = SEND_M7;
1189189251Ssam	return WPS_CONTINUE;
1190189251Ssam}
1191189251Ssam
1192189251Ssam
1193189251Ssamstatic enum wps_process_res wps_process_m8(struct wps_data *wps,
1194189251Ssam					   const struct wpabuf *msg,
1195189251Ssam					   struct wps_parse_attr *attr)
1196189251Ssam{
1197189251Ssam	struct wpabuf *decrypted;
1198189251Ssam	struct wps_parse_attr eattr;
1199189251Ssam
1200189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M8");
1201189251Ssam
1202189251Ssam	if (wps->state != RECV_M8) {
1203189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1204189251Ssam			   "receiving M8", wps->state);
1205189251Ssam		wps->state = SEND_WSC_NACK;
1206189251Ssam		return WPS_CONTINUE;
1207189251Ssam	}
1208189251Ssam
1209189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1210189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg)) {
1211189251Ssam		wps->state = SEND_WSC_NACK;
1212189251Ssam		return WPS_CONTINUE;
1213189251Ssam	}
1214189251Ssam
1215252726Srpaulo	if (wps->wps->ap && wps->wps->ap_setup_locked) {
1216252726Srpaulo		/*
1217252726Srpaulo		 * Stop here if special ap_setup_locked == 2 mode allowed the
1218252726Srpaulo		 * protocol to continue beyond M2. This allows ER to learn the
1219252726Srpaulo		 * current AP settings without changing them.
1220252726Srpaulo		 */
1221252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1222252726Srpaulo			   "registration of a new Registrar");
1223252726Srpaulo		wps->config_error = WPS_CFG_SETUP_LOCKED;
1224252726Srpaulo		wps->state = SEND_WSC_NACK;
1225252726Srpaulo		return WPS_CONTINUE;
1226252726Srpaulo	}
1227252726Srpaulo
1228189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1229189251Ssam					      attr->encr_settings_len);
1230189251Ssam	if (decrypted == NULL) {
1231189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1232189251Ssam			   "Settings attribute");
1233189251Ssam		wps->state = SEND_WSC_NACK;
1234189251Ssam		return WPS_CONTINUE;
1235189251Ssam	}
1236189251Ssam
1237252726Srpaulo	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1238252726Srpaulo				 attr->version2 != NULL) < 0) {
1239252726Srpaulo		wpabuf_free(decrypted);
1240252726Srpaulo		wps->state = SEND_WSC_NACK;
1241252726Srpaulo		return WPS_CONTINUE;
1242252726Srpaulo	}
1243252726Srpaulo
1244189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1245189251Ssam		   "attribute");
1246189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1247189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1248189251Ssam	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1249252726Srpaulo			      eattr.num_cred, attr->version2 != NULL) ||
1250252726Srpaulo	    wps_process_ap_settings_e(wps, &eattr, decrypted,
1251252726Srpaulo				      attr->version2 != NULL)) {
1252189251Ssam		wpabuf_free(decrypted);
1253189251Ssam		wps->state = SEND_WSC_NACK;
1254189251Ssam		return WPS_CONTINUE;
1255189251Ssam	}
1256189251Ssam	wpabuf_free(decrypted);
1257189251Ssam
1258189251Ssam	wps->state = WPS_MSG_DONE;
1259189251Ssam	return WPS_CONTINUE;
1260189251Ssam}
1261189251Ssam
1262189251Ssam
1263189251Ssamstatic enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
1264189251Ssam						const struct wpabuf *msg)
1265189251Ssam{
1266189251Ssam	struct wps_parse_attr attr;
1267189251Ssam	enum wps_process_res ret = WPS_CONTINUE;
1268189251Ssam
1269189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
1270189251Ssam
1271189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1272189251Ssam		return WPS_FAILURE;
1273189251Ssam
1274189251Ssam	if (attr.enrollee_nonce == NULL ||
1275252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1276189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1277189251Ssam		return WPS_FAILURE;
1278189251Ssam	}
1279189251Ssam
1280189251Ssam	if (attr.msg_type == NULL) {
1281189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1282252726Srpaulo		wps->state = SEND_WSC_NACK;
1283252726Srpaulo		return WPS_CONTINUE;
1284189251Ssam	}
1285189251Ssam
1286189251Ssam	switch (*attr.msg_type) {
1287189251Ssam	case WPS_M2:
1288252726Srpaulo		if (wps_validate_m2(msg) < 0)
1289252726Srpaulo			return WPS_FAILURE;
1290189251Ssam		ret = wps_process_m2(wps, msg, &attr);
1291189251Ssam		break;
1292189251Ssam	case WPS_M2D:
1293252726Srpaulo		if (wps_validate_m2d(msg) < 0)
1294252726Srpaulo			return WPS_FAILURE;
1295189251Ssam		ret = wps_process_m2d(wps, &attr);
1296189251Ssam		break;
1297189251Ssam	case WPS_M4:
1298252726Srpaulo		if (wps_validate_m4(msg) < 0)
1299252726Srpaulo			return WPS_FAILURE;
1300189251Ssam		ret = wps_process_m4(wps, msg, &attr);
1301189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1302252726Srpaulo			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
1303281806Srpaulo				       wps->error_indication,
1304281806Srpaulo				       wps->peer_dev.mac_addr);
1305189251Ssam		break;
1306189251Ssam	case WPS_M6:
1307252726Srpaulo		if (wps_validate_m6(msg) < 0)
1308252726Srpaulo			return WPS_FAILURE;
1309189251Ssam		ret = wps_process_m6(wps, msg, &attr);
1310189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1311252726Srpaulo			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
1312281806Srpaulo				       wps->error_indication,
1313281806Srpaulo				       wps->peer_dev.mac_addr);
1314189251Ssam		break;
1315189251Ssam	case WPS_M8:
1316252726Srpaulo		if (wps_validate_m8(msg) < 0)
1317252726Srpaulo			return WPS_FAILURE;
1318189251Ssam		ret = wps_process_m8(wps, msg, &attr);
1319189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1320252726Srpaulo			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
1321281806Srpaulo				       wps->error_indication,
1322281806Srpaulo				       wps->peer_dev.mac_addr);
1323189251Ssam		break;
1324189251Ssam	default:
1325189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
1326189251Ssam			   *attr.msg_type);
1327189251Ssam		return WPS_FAILURE;
1328189251Ssam	}
1329189251Ssam
1330189251Ssam	/*
1331189251Ssam	 * Save a copy of the last message for Authenticator derivation if we
1332189251Ssam	 * are continuing. However, skip M2D since it is not authenticated and
1333189251Ssam	 * neither is the ACK/NACK response frame. This allows the possibly
1334189251Ssam	 * following M2 to be processed correctly by using the previously sent
1335189251Ssam	 * M1 in Authenticator derivation.
1336189251Ssam	 */
1337189251Ssam	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
1338189251Ssam		/* Save a copy of the last message for Authenticator derivation
1339189251Ssam		 */
1340189251Ssam		wpabuf_free(wps->last_msg);
1341189251Ssam		wps->last_msg = wpabuf_dup(msg);
1342189251Ssam	}
1343189251Ssam
1344189251Ssam	return ret;
1345189251Ssam}
1346189251Ssam
1347189251Ssam
1348189251Ssamstatic enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
1349189251Ssam						const struct wpabuf *msg)
1350189251Ssam{
1351189251Ssam	struct wps_parse_attr attr;
1352189251Ssam
1353189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
1354189251Ssam
1355189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1356189251Ssam		return WPS_FAILURE;
1357189251Ssam
1358189251Ssam	if (attr.msg_type == NULL) {
1359189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1360189251Ssam		return WPS_FAILURE;
1361189251Ssam	}
1362189251Ssam
1363189251Ssam	if (*attr.msg_type != WPS_WSC_ACK) {
1364189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1365189251Ssam			   *attr.msg_type);
1366189251Ssam		return WPS_FAILURE;
1367189251Ssam	}
1368189251Ssam
1369189251Ssam	if (attr.registrar_nonce == NULL ||
1370252726Srpaulo	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1371189251Ssam	{
1372189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1373189251Ssam		return WPS_FAILURE;
1374189251Ssam	}
1375189251Ssam
1376189251Ssam	if (attr.enrollee_nonce == NULL ||
1377252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1378189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1379189251Ssam		return WPS_FAILURE;
1380189251Ssam	}
1381189251Ssam
1382189251Ssam	if (wps->state == RECV_ACK && wps->wps->ap) {
1383189251Ssam		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
1384189251Ssam			   "completed successfully");
1385281806Srpaulo		wps_success_event(wps->wps, wps->peer_dev.mac_addr);
1386189251Ssam		wps->state = WPS_FINISHED;
1387189251Ssam		return WPS_DONE;
1388189251Ssam	}
1389189251Ssam
1390189251Ssam	return WPS_FAILURE;
1391189251Ssam}
1392189251Ssam
1393189251Ssam
1394189251Ssamstatic enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
1395189251Ssam						 const struct wpabuf *msg)
1396189251Ssam{
1397189251Ssam	struct wps_parse_attr attr;
1398252726Srpaulo	u16 config_error;
1399189251Ssam
1400189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
1401189251Ssam
1402189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1403189251Ssam		return WPS_FAILURE;
1404189251Ssam
1405189251Ssam	if (attr.msg_type == NULL) {
1406189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1407189251Ssam		return WPS_FAILURE;
1408189251Ssam	}
1409189251Ssam
1410189251Ssam	if (*attr.msg_type != WPS_WSC_NACK) {
1411189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1412189251Ssam			   *attr.msg_type);
1413189251Ssam		return WPS_FAILURE;
1414189251Ssam	}
1415189251Ssam
1416189251Ssam	if (attr.registrar_nonce == NULL ||
1417252726Srpaulo	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1418189251Ssam	{
1419189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1420189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
1421189251Ssam			    attr.registrar_nonce, WPS_NONCE_LEN);
1422189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
1423189251Ssam			    wps->nonce_r, WPS_NONCE_LEN);
1424189251Ssam		return WPS_FAILURE;
1425189251Ssam	}
1426189251Ssam
1427189251Ssam	if (attr.enrollee_nonce == NULL ||
1428252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1429189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1430189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
1431189251Ssam			    attr.enrollee_nonce, WPS_NONCE_LEN);
1432189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
1433189251Ssam			    wps->nonce_e, WPS_NONCE_LEN);
1434189251Ssam		return WPS_FAILURE;
1435189251Ssam	}
1436189251Ssam
1437189251Ssam	if (attr.config_error == NULL) {
1438189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
1439189251Ssam			   "in WSC_NACK");
1440189251Ssam		return WPS_FAILURE;
1441189251Ssam	}
1442189251Ssam
1443252726Srpaulo	config_error = WPA_GET_BE16(attr.config_error);
1444189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1445252726Srpaulo		   "Configuration Error %d", config_error);
1446189251Ssam
1447189251Ssam	switch (wps->state) {
1448189251Ssam	case RECV_M4:
1449252726Srpaulo		wps_fail_event(wps->wps, WPS_M3, config_error,
1450281806Srpaulo			       wps->error_indication, wps->peer_dev.mac_addr);
1451189251Ssam		break;
1452189251Ssam	case RECV_M6:
1453252726Srpaulo		wps_fail_event(wps->wps, WPS_M5, config_error,
1454281806Srpaulo			       wps->error_indication, wps->peer_dev.mac_addr);
1455189251Ssam		break;
1456189251Ssam	case RECV_M8:
1457252726Srpaulo		wps_fail_event(wps->wps, WPS_M7, config_error,
1458281806Srpaulo			       wps->error_indication, wps->peer_dev.mac_addr);
1459189251Ssam		break;
1460189251Ssam	default:
1461189251Ssam		break;
1462189251Ssam	}
1463189251Ssam
1464189251Ssam	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
1465189251Ssam	 * Enrollee is Authenticator */
1466189251Ssam	wps->state = SEND_WSC_NACK;
1467189251Ssam
1468189251Ssam	return WPS_FAILURE;
1469189251Ssam}
1470189251Ssam
1471189251Ssam
1472189251Ssamenum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
1473189251Ssam					      enum wsc_op_code op_code,
1474189251Ssam					      const struct wpabuf *msg)
1475189251Ssam{
1476189251Ssam
1477189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
1478189251Ssam		   "op_code=%d)",
1479189251Ssam		   (unsigned long) wpabuf_len(msg), op_code);
1480189251Ssam
1481209158Srpaulo	if (op_code == WSC_UPnP) {
1482209158Srpaulo		/* Determine the OpCode based on message type attribute */
1483209158Srpaulo		struct wps_parse_attr attr;
1484209158Srpaulo		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
1485209158Srpaulo			if (*attr.msg_type == WPS_WSC_ACK)
1486209158Srpaulo				op_code = WSC_ACK;
1487209158Srpaulo			else if (*attr.msg_type == WPS_WSC_NACK)
1488209158Srpaulo				op_code = WSC_NACK;
1489209158Srpaulo		}
1490209158Srpaulo	}
1491209158Srpaulo
1492189251Ssam	switch (op_code) {
1493189251Ssam	case WSC_MSG:
1494189251Ssam	case WSC_UPnP:
1495189251Ssam		return wps_process_wsc_msg(wps, msg);
1496189251Ssam	case WSC_ACK:
1497252726Srpaulo		if (wps_validate_wsc_ack(msg) < 0)
1498252726Srpaulo			return WPS_FAILURE;
1499189251Ssam		return wps_process_wsc_ack(wps, msg);
1500189251Ssam	case WSC_NACK:
1501252726Srpaulo		if (wps_validate_wsc_nack(msg) < 0)
1502252726Srpaulo			return WPS_FAILURE;
1503189251Ssam		return wps_process_wsc_nack(wps, msg);
1504189251Ssam	default:
1505189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
1506189251Ssam		return WPS_FAILURE;
1507189251Ssam	}
1508189251Ssam}
1509