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_mac_addr(struct wps_data *wps, struct wpabuf *msg)
20189251Ssam{
21189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address");
22189251Ssam	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
23189251Ssam	wpabuf_put_be16(msg, ETH_ALEN);
24189251Ssam	wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
25189251Ssam	return 0;
26189251Ssam}
27189251Ssam
28189251Ssam
29189251Ssamstatic int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
30189251Ssam{
31189251Ssam	u8 state;
32189251Ssam	if (wps->wps->ap)
33189251Ssam		state = wps->wps->wps_state;
34189251Ssam	else
35189251Ssam		state = WPS_STATE_NOT_CONFIGURED;
36189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
37189251Ssam		   state);
38189251Ssam	wpabuf_put_be16(msg, ATTR_WPS_STATE);
39189251Ssam	wpabuf_put_be16(msg, 1);
40209158Srpaulo	wpabuf_put_u8(msg, state);
41189251Ssam	return 0;
42189251Ssam}
43189251Ssam
44189251Ssam
45189251Ssamstatic int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
46189251Ssam{
47189251Ssam	u8 *hash;
48189251Ssam	const u8 *addr[4];
49189251Ssam	size_t len[4];
50189251Ssam
51252726Srpaulo	if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
52189251Ssam		return -1;
53189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
54189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
55189251Ssam		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
56189251Ssam
57189251Ssam	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
58189251Ssam		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
59189251Ssam			   "E-Hash derivation");
60189251Ssam		return -1;
61189251Ssam	}
62189251Ssam
63189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
64189251Ssam	wpabuf_put_be16(msg, ATTR_E_HASH1);
65189251Ssam	wpabuf_put_be16(msg, SHA256_MAC_LEN);
66189251Ssam	hash = wpabuf_put(msg, SHA256_MAC_LEN);
67189251Ssam	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
68189251Ssam	addr[0] = wps->snonce;
69189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
70189251Ssam	addr[1] = wps->psk1;
71189251Ssam	len[1] = WPS_PSK_LEN;
72189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
73189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
74189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
75189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
76189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
77189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
78189251Ssam
79189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
80189251Ssam	wpabuf_put_be16(msg, ATTR_E_HASH2);
81189251Ssam	wpabuf_put_be16(msg, SHA256_MAC_LEN);
82189251Ssam	hash = wpabuf_put(msg, SHA256_MAC_LEN);
83189251Ssam	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
84189251Ssam	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
85189251Ssam	addr[1] = wps->psk2;
86189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
87189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
88189251Ssam
89189251Ssam	return 0;
90189251Ssam}
91189251Ssam
92189251Ssam
93189251Ssamstatic int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
94189251Ssam{
95189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
96189251Ssam	wpabuf_put_be16(msg, ATTR_E_SNONCE1);
97189251Ssam	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
98189251Ssam	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
99189251Ssam	return 0;
100189251Ssam}
101189251Ssam
102189251Ssam
103189251Ssamstatic int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
104189251Ssam{
105189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
106189251Ssam	wpabuf_put_be16(msg, ATTR_E_SNONCE2);
107189251Ssam	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
108189251Ssam	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
109189251Ssam			WPS_SECRET_NONCE_LEN);
110189251Ssam	return 0;
111189251Ssam}
112189251Ssam
113189251Ssam
114189251Ssamstatic struct wpabuf * wps_build_m1(struct wps_data *wps)
115189251Ssam{
116189251Ssam	struct wpabuf *msg;
117252726Srpaulo	u16 config_methods;
118189251Ssam
119252726Srpaulo	if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
120189251Ssam		return NULL;
121189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
122189251Ssam		    wps->nonce_e, WPS_NONCE_LEN);
123189251Ssam
124189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
125189251Ssam	msg = wpabuf_alloc(1000);
126189251Ssam	if (msg == NULL)
127189251Ssam		return NULL;
128189251Ssam
129252726Srpaulo	config_methods = wps->wps->config_methods;
130252726Srpaulo	if (wps->wps->ap && !wps->pbc_in_m1 &&
131252726Srpaulo	    (wps->dev_password_len != 0 ||
132252726Srpaulo	     (config_methods & WPS_CONFIG_DISPLAY))) {
133252726Srpaulo		/*
134252726Srpaulo		 * These are the methods that the AP supports as an Enrollee
135252726Srpaulo		 * for adding external Registrars, so remove PushButton.
136252726Srpaulo		 *
137252726Srpaulo		 * As a workaround for Windows 7 mechanism for probing WPS
138252726Srpaulo		 * capabilities from M1, leave PushButton option if no PIN
139252726Srpaulo		 * method is available or if WPS configuration enables PBC
140252726Srpaulo		 * workaround.
141252726Srpaulo		 */
142252726Srpaulo		config_methods &= ~WPS_CONFIG_PUSHBUTTON;
143252726Srpaulo#ifdef CONFIG_WPS2
144252726Srpaulo		config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
145252726Srpaulo				    WPS_CONFIG_PHY_PUSHBUTTON);
146252726Srpaulo#endif /* CONFIG_WPS2 */
147252726Srpaulo	}
148252726Srpaulo
149189251Ssam	if (wps_build_version(msg) ||
150189251Ssam	    wps_build_msg_type(msg, WPS_M1) ||
151189251Ssam	    wps_build_uuid_e(msg, wps->uuid_e) ||
152189251Ssam	    wps_build_mac_addr(wps, msg) ||
153189251Ssam	    wps_build_enrollee_nonce(wps, msg) ||
154189251Ssam	    wps_build_public_key(wps, msg) ||
155189251Ssam	    wps_build_auth_type_flags(wps, msg) ||
156189251Ssam	    wps_build_encr_type_flags(wps, msg) ||
157189251Ssam	    wps_build_conn_type_flags(wps, msg) ||
158252726Srpaulo	    wps_build_config_methods(msg, config_methods) ||
159189251Ssam	    wps_build_wps_state(wps, msg) ||
160189251Ssam	    wps_build_device_attrs(&wps->wps->dev, msg) ||
161189251Ssam	    wps_build_rf_bands(&wps->wps->dev, msg) ||
162189251Ssam	    wps_build_assoc_state(wps, msg) ||
163189251Ssam	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
164189251Ssam	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
165252726Srpaulo	    wps_build_os_version(&wps->wps->dev, msg) ||
166252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
167252726Srpaulo	    wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
168189251Ssam		wpabuf_free(msg);
169189251Ssam		return NULL;
170189251Ssam	}
171189251Ssam
172189251Ssam	wps->state = RECV_M2;
173189251Ssam	return msg;
174189251Ssam}
175189251Ssam
176189251Ssam
177189251Ssamstatic struct wpabuf * wps_build_m3(struct wps_data *wps)
178189251Ssam{
179189251Ssam	struct wpabuf *msg;
180189251Ssam
181189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
182189251Ssam
183189251Ssam	if (wps->dev_password == NULL) {
184189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
185189251Ssam		return NULL;
186189251Ssam	}
187189251Ssam	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
188189251Ssam
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) ||
197252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 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) ||
230252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
231189251Ssam	    wps_build_authenticator(wps, msg)) {
232189251Ssam		wpabuf_free(plain);
233189251Ssam		wpabuf_free(msg);
234189251Ssam		return NULL;
235189251Ssam	}
236189251Ssam	wpabuf_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{
255252726Srpaulo	u16 auth_type = wps->wps->auth_types;
256252726Srpaulo
257252726Srpaulo	/* Select the best authentication type */
258252726Srpaulo	if (auth_type & WPS_AUTH_WPA2PSK)
259252726Srpaulo		auth_type = WPS_AUTH_WPA2PSK;
260252726Srpaulo	else if (auth_type & WPS_AUTH_WPAPSK)
261252726Srpaulo		auth_type = WPS_AUTH_WPAPSK;
262252726Srpaulo	else if (auth_type & WPS_AUTH_OPEN)
263252726Srpaulo		auth_type = WPS_AUTH_OPEN;
264252726Srpaulo	else if (auth_type & WPS_AUTH_SHARED)
265252726Srpaulo		auth_type = WPS_AUTH_SHARED;
266252726Srpaulo
267252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
268189251Ssam	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
269189251Ssam	wpabuf_put_be16(msg, 2);
270252726Srpaulo	wpabuf_put_be16(msg, auth_type);
271189251Ssam	return 0;
272189251Ssam}
273189251Ssam
274189251Ssam
275189251Ssamstatic int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
276189251Ssam{
277252726Srpaulo	u16 encr_type = wps->wps->encr_types;
278252726Srpaulo
279252726Srpaulo	/* Select the best encryption type */
280252726Srpaulo	if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
281252726Srpaulo		if (encr_type & WPS_ENCR_AES)
282252726Srpaulo			encr_type = WPS_ENCR_AES;
283252726Srpaulo		else if (encr_type & WPS_ENCR_TKIP)
284252726Srpaulo			encr_type = WPS_ENCR_TKIP;
285252726Srpaulo	} else {
286252726Srpaulo		if (encr_type & WPS_ENCR_WEP)
287252726Srpaulo			encr_type = WPS_ENCR_WEP;
288252726Srpaulo		else if (encr_type & WPS_ENCR_NONE)
289252726Srpaulo			encr_type = WPS_ENCR_NONE;
290252726Srpaulo	}
291252726Srpaulo
292252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
293189251Ssam	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
294189251Ssam	wpabuf_put_be16(msg, 2);
295252726Srpaulo	wpabuf_put_be16(msg, encr_type);
296189251Ssam	return 0;
297189251Ssam}
298189251Ssam
299189251Ssam
300189251Ssamstatic int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
301189251Ssam{
302189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
303189251Ssam	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
304189251Ssam	wpabuf_put_be16(msg, wps->wps->network_key_len);
305189251Ssam	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
306189251Ssam	return 0;
307189251Ssam}
308189251Ssam
309189251Ssam
310189251Ssamstatic int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
311189251Ssam{
312189251Ssam	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
313189251Ssam	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
314189251Ssam	wpabuf_put_be16(msg, ETH_ALEN);
315189251Ssam	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
316189251Ssam	return 0;
317189251Ssam}
318189251Ssam
319189251Ssam
320189251Ssamstatic int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
321189251Ssam{
322189251Ssam	if (wps->wps->ap_settings) {
323189251Ssam		wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
324189251Ssam		wpabuf_put_data(plain, wps->wps->ap_settings,
325189251Ssam				wps->wps->ap_settings_len);
326189251Ssam		return 0;
327189251Ssam	}
328189251Ssam
329189251Ssam	return wps_build_cred_ssid(wps, plain) ||
330189251Ssam		wps_build_cred_mac_addr(wps, plain) ||
331189251Ssam		wps_build_cred_auth_type(wps, plain) ||
332189251Ssam		wps_build_cred_encr_type(wps, plain) ||
333189251Ssam		wps_build_cred_network_key(wps, plain);
334189251Ssam}
335189251Ssam
336189251Ssam
337189251Ssamstatic struct wpabuf * wps_build_m7(struct wps_data *wps)
338189251Ssam{
339189251Ssam	struct wpabuf *msg, *plain;
340189251Ssam
341189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
342189251Ssam
343189251Ssam	plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
344189251Ssam	if (plain == NULL)
345189251Ssam		return NULL;
346189251Ssam
347189251Ssam	msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
348189251Ssam	if (msg == NULL) {
349189251Ssam		wpabuf_free(plain);
350189251Ssam		return NULL;
351189251Ssam	}
352189251Ssam
353189251Ssam	if (wps_build_version(msg) ||
354189251Ssam	    wps_build_msg_type(msg, WPS_M7) ||
355189251Ssam	    wps_build_registrar_nonce(wps, msg) ||
356189251Ssam	    wps_build_e_snonce2(wps, plain) ||
357189251Ssam	    (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
358189251Ssam	    wps_build_key_wrap_auth(wps, plain) ||
359189251Ssam	    wps_build_encr_settings(wps, msg, plain) ||
360252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
361189251Ssam	    wps_build_authenticator(wps, msg)) {
362189251Ssam		wpabuf_free(plain);
363189251Ssam		wpabuf_free(msg);
364189251Ssam		return NULL;
365189251Ssam	}
366189251Ssam	wpabuf_free(plain);
367189251Ssam
368214734Srpaulo	if (wps->wps->ap && wps->wps->registrar) {
369214734Srpaulo		/*
370214734Srpaulo		 * If the Registrar is only learning our current configuration,
371214734Srpaulo		 * it may not continue protocol run to successful completion.
372214734Srpaulo		 * Store information here to make sure it remains available.
373214734Srpaulo		 */
374214734Srpaulo		wps_device_store(wps->wps->registrar, &wps->peer_dev,
375214734Srpaulo				 wps->uuid_r);
376214734Srpaulo	}
377214734Srpaulo
378189251Ssam	wps->state = RECV_M8;
379189251Ssam	return msg;
380189251Ssam}
381189251Ssam
382189251Ssam
383189251Ssamstatic struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
384189251Ssam{
385189251Ssam	struct wpabuf *msg;
386189251Ssam
387189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
388189251Ssam
389189251Ssam	msg = wpabuf_alloc(1000);
390189251Ssam	if (msg == NULL)
391189251Ssam		return NULL;
392189251Ssam
393189251Ssam	if (wps_build_version(msg) ||
394189251Ssam	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
395189251Ssam	    wps_build_enrollee_nonce(wps, msg) ||
396252726Srpaulo	    wps_build_registrar_nonce(wps, msg) ||
397252726Srpaulo	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
398189251Ssam		wpabuf_free(msg);
399189251Ssam		return NULL;
400189251Ssam	}
401189251Ssam
402189251Ssam	if (wps->wps->ap)
403189251Ssam		wps->state = RECV_ACK;
404189251Ssam	else {
405189251Ssam		wps_success_event(wps->wps);
406189251Ssam		wps->state = WPS_FINISHED;
407189251Ssam	}
408189251Ssam	return msg;
409189251Ssam}
410189251Ssam
411189251Ssam
412189251Ssamstruct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
413189251Ssam				     enum wsc_op_code *op_code)
414189251Ssam{
415189251Ssam	struct wpabuf *msg;
416189251Ssam
417189251Ssam	switch (wps->state) {
418189251Ssam	case SEND_M1:
419189251Ssam		msg = wps_build_m1(wps);
420189251Ssam		*op_code = WSC_MSG;
421189251Ssam		break;
422189251Ssam	case SEND_M3:
423189251Ssam		msg = wps_build_m3(wps);
424189251Ssam		*op_code = WSC_MSG;
425189251Ssam		break;
426189251Ssam	case SEND_M5:
427189251Ssam		msg = wps_build_m5(wps);
428189251Ssam		*op_code = WSC_MSG;
429189251Ssam		break;
430189251Ssam	case SEND_M7:
431189251Ssam		msg = wps_build_m7(wps);
432189251Ssam		*op_code = WSC_MSG;
433189251Ssam		break;
434189251Ssam	case RECEIVED_M2D:
435189251Ssam		if (wps->wps->ap) {
436189251Ssam			msg = wps_build_wsc_nack(wps);
437189251Ssam			*op_code = WSC_NACK;
438189251Ssam			break;
439189251Ssam		}
440189251Ssam		msg = wps_build_wsc_ack(wps);
441189251Ssam		*op_code = WSC_ACK;
442189251Ssam		if (msg) {
443189251Ssam			/* Another M2/M2D may be received */
444189251Ssam			wps->state = RECV_M2;
445189251Ssam		}
446189251Ssam		break;
447189251Ssam	case SEND_WSC_NACK:
448189251Ssam		msg = wps_build_wsc_nack(wps);
449189251Ssam		*op_code = WSC_NACK;
450189251Ssam		break;
451189251Ssam	case WPS_MSG_DONE:
452189251Ssam		msg = wps_build_wsc_done(wps);
453189251Ssam		*op_code = WSC_Done;
454189251Ssam		break;
455189251Ssam	default:
456189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
457189251Ssam			   "a message", wps->state);
458189251Ssam		msg = NULL;
459189251Ssam		break;
460189251Ssam	}
461189251Ssam
462189251Ssam	if (*op_code == WSC_MSG && msg) {
463189251Ssam		/* Save a copy of the last message for Authenticator derivation
464189251Ssam		 */
465189251Ssam		wpabuf_free(wps->last_msg);
466189251Ssam		wps->last_msg = wpabuf_dup(msg);
467189251Ssam	}
468189251Ssam
469189251Ssam	return msg;
470189251Ssam}
471189251Ssam
472189251Ssam
473189251Ssamstatic int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
474189251Ssam{
475189251Ssam	if (r_nonce == NULL) {
476189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
477189251Ssam		return -1;
478189251Ssam	}
479189251Ssam
480189251Ssam	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
481189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
482189251Ssam		    wps->nonce_r, WPS_NONCE_LEN);
483189251Ssam
484189251Ssam	return 0;
485189251Ssam}
486189251Ssam
487189251Ssam
488189251Ssamstatic int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
489189251Ssam{
490189251Ssam	if (e_nonce == NULL) {
491189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
492189251Ssam		return -1;
493189251Ssam	}
494189251Ssam
495189251Ssam	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
496189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
497189251Ssam		return -1;
498189251Ssam	}
499189251Ssam
500189251Ssam	return 0;
501189251Ssam}
502189251Ssam
503189251Ssam
504189251Ssamstatic int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
505189251Ssam{
506189251Ssam	if (uuid_r == NULL) {
507189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
508189251Ssam		return -1;
509189251Ssam	}
510189251Ssam
511189251Ssam	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
512189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
513189251Ssam
514189251Ssam	return 0;
515189251Ssam}
516189251Ssam
517189251Ssam
518189251Ssamstatic int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
519189251Ssam			      size_t pk_len)
520189251Ssam{
521189251Ssam	if (pk == NULL || pk_len == 0) {
522189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
523189251Ssam		return -1;
524189251Ssam	}
525189251Ssam
526189251Ssam	wpabuf_free(wps->dh_pubkey_r);
527189251Ssam	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
528189251Ssam	if (wps->dh_pubkey_r == NULL)
529189251Ssam		return -1;
530189251Ssam
531189251Ssam	if (wps_derive_keys(wps) < 0)
532189251Ssam		return -1;
533189251Ssam
534189251Ssam	return 0;
535189251Ssam}
536189251Ssam
537189251Ssam
538189251Ssamstatic int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
539189251Ssam{
540189251Ssam	if (r_hash1 == NULL) {
541189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
542189251Ssam		return -1;
543189251Ssam	}
544189251Ssam
545189251Ssam	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
546189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
547189251Ssam
548189251Ssam	return 0;
549189251Ssam}
550189251Ssam
551189251Ssam
552189251Ssamstatic int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
553189251Ssam{
554189251Ssam	if (r_hash2 == NULL) {
555189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
556189251Ssam		return -1;
557189251Ssam	}
558189251Ssam
559189251Ssam	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
560189251Ssam	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
561189251Ssam
562189251Ssam	return 0;
563189251Ssam}
564189251Ssam
565189251Ssam
566189251Ssamstatic int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
567189251Ssam{
568189251Ssam	u8 hash[SHA256_MAC_LEN];
569189251Ssam	const u8 *addr[4];
570189251Ssam	size_t len[4];
571189251Ssam
572189251Ssam	if (r_snonce1 == NULL) {
573189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
574189251Ssam		return -1;
575189251Ssam	}
576189251Ssam
577189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
578189251Ssam			WPS_SECRET_NONCE_LEN);
579189251Ssam
580189251Ssam	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
581189251Ssam	addr[0] = r_snonce1;
582189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
583189251Ssam	addr[1] = wps->psk1;
584189251Ssam	len[1] = WPS_PSK_LEN;
585189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
586189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
587189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
588189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
589189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
590189251Ssam
591189251Ssam	if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
592189251Ssam		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
593189251Ssam			   "not match with the pre-committed value");
594189251Ssam		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
595189251Ssam		wps_pwd_auth_fail_event(wps->wps, 1, 1);
596189251Ssam		return -1;
597189251Ssam	}
598189251Ssam
599189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
600189251Ssam		   "half of the device password");
601189251Ssam
602189251Ssam	return 0;
603189251Ssam}
604189251Ssam
605189251Ssam
606189251Ssamstatic int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
607189251Ssam{
608189251Ssam	u8 hash[SHA256_MAC_LEN];
609189251Ssam	const u8 *addr[4];
610189251Ssam	size_t len[4];
611189251Ssam
612189251Ssam	if (r_snonce2 == NULL) {
613189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
614189251Ssam		return -1;
615189251Ssam	}
616189251Ssam
617189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
618189251Ssam			WPS_SECRET_NONCE_LEN);
619189251Ssam
620189251Ssam	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
621189251Ssam	addr[0] = r_snonce2;
622189251Ssam	len[0] = WPS_SECRET_NONCE_LEN;
623189251Ssam	addr[1] = wps->psk2;
624189251Ssam	len[1] = WPS_PSK_LEN;
625189251Ssam	addr[2] = wpabuf_head(wps->dh_pubkey_e);
626189251Ssam	len[2] = wpabuf_len(wps->dh_pubkey_e);
627189251Ssam	addr[3] = wpabuf_head(wps->dh_pubkey_r);
628189251Ssam	len[3] = wpabuf_len(wps->dh_pubkey_r);
629189251Ssam	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
630189251Ssam
631189251Ssam	if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
632189251Ssam		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
633189251Ssam			   "not match with the pre-committed value");
634189251Ssam		wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
635189251Ssam		wps_pwd_auth_fail_event(wps->wps, 1, 2);
636189251Ssam		return -1;
637189251Ssam	}
638189251Ssam
639189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
640189251Ssam		   "half of the device password");
641189251Ssam
642189251Ssam	return 0;
643189251Ssam}
644189251Ssam
645189251Ssam
646189251Ssamstatic int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
647252726Srpaulo			      size_t cred_len, int wps2)
648189251Ssam{
649189251Ssam	struct wps_parse_attr attr;
650189251Ssam	struct wpabuf msg;
651252726Srpaulo	int ret = 0;
652189251Ssam
653189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
654189251Ssam	os_memset(&wps->cred, 0, sizeof(wps->cred));
655189251Ssam	wpabuf_set(&msg, cred, cred_len);
656189251Ssam	if (wps_parse_msg(&msg, &attr) < 0 ||
657189251Ssam	    wps_process_cred(&attr, &wps->cred))
658189251Ssam		return -1;
659189251Ssam
660209158Srpaulo	if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
661209158Srpaulo	    0) {
662209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
663209158Srpaulo			   MACSTR ") does not match with own address (" MACSTR
664209158Srpaulo			   ")", MAC2STR(wps->cred.mac_addr),
665209158Srpaulo			   MAC2STR(wps->wps->dev.mac_addr));
666209158Srpaulo		/*
667209158Srpaulo		 * In theory, this could be consider fatal error, but there are
668209158Srpaulo		 * number of deployed implementations using other address here
669209158Srpaulo		 * due to unclarity in the specification. For interoperability
670209158Srpaulo		 * reasons, allow this to be processed since we do not really
671209158Srpaulo		 * use the MAC Address information for anything.
672209158Srpaulo		 */
673252726Srpaulo#ifdef CONFIG_WPS_STRICT
674252726Srpaulo		if (wps2) {
675252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
676252726Srpaulo				   "MAC Address in AP Settings");
677252726Srpaulo			return -1;
678252726Srpaulo		}
679252726Srpaulo#endif /* CONFIG_WPS_STRICT */
680209158Srpaulo	}
681209158Srpaulo
682252726Srpaulo#ifdef CONFIG_WPS2
683252726Srpaulo	if (!(wps->cred.encr_type &
684252726Srpaulo	      (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
685252726Srpaulo		if (wps->cred.encr_type & WPS_ENCR_WEP) {
686252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Reject Credential "
687252726Srpaulo				   "due to WEP configuration");
688252726Srpaulo			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
689252726Srpaulo			return -2;
690252726Srpaulo		}
691252726Srpaulo
692252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
693252726Srpaulo			   "invalid encr_type 0x%x", wps->cred.encr_type);
694252726Srpaulo		return -1;
695252726Srpaulo	}
696252726Srpaulo#endif /* CONFIG_WPS2 */
697252726Srpaulo
698189251Ssam	if (wps->wps->cred_cb) {
699189251Ssam		wps->cred.cred_attr = cred - 4;
700189251Ssam		wps->cred.cred_attr_len = cred_len + 4;
701252726Srpaulo		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
702189251Ssam		wps->cred.cred_attr = NULL;
703189251Ssam		wps->cred.cred_attr_len = 0;
704189251Ssam	}
705189251Ssam
706252726Srpaulo	return ret;
707189251Ssam}
708189251Ssam
709189251Ssam
710189251Ssamstatic int wps_process_creds(struct wps_data *wps, const u8 *cred[],
711252726Srpaulo			     size_t cred_len[], size_t num_cred, int wps2)
712189251Ssam{
713189251Ssam	size_t i;
714252726Srpaulo	int ok = 0;
715189251Ssam
716189251Ssam	if (wps->wps->ap)
717189251Ssam		return 0;
718189251Ssam
719189251Ssam	if (num_cred == 0) {
720189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
721189251Ssam			   "received");
722189251Ssam		return -1;
723189251Ssam	}
724189251Ssam
725189251Ssam	for (i = 0; i < num_cred; i++) {
726252726Srpaulo		int res;
727252726Srpaulo		res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
728252726Srpaulo		if (res == 0)
729252726Srpaulo			ok++;
730252726Srpaulo		else if (res == -2)
731252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
732252726Srpaulo		else
733189251Ssam			return -1;
734189251Ssam	}
735189251Ssam
736252726Srpaulo	if (ok == 0) {
737252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
738252726Srpaulo			   "received");
739252726Srpaulo		return -1;
740252726Srpaulo	}
741252726Srpaulo
742189251Ssam	return 0;
743189251Ssam}
744189251Ssam
745189251Ssam
746189251Ssamstatic int wps_process_ap_settings_e(struct wps_data *wps,
747189251Ssam				     struct wps_parse_attr *attr,
748252726Srpaulo				     struct wpabuf *attrs, int wps2)
749189251Ssam{
750189251Ssam	struct wps_credential cred;
751189251Ssam
752189251Ssam	if (!wps->wps->ap)
753189251Ssam		return 0;
754189251Ssam
755189251Ssam	if (wps_process_ap_settings(attr, &cred) < 0)
756189251Ssam		return -1;
757189251Ssam
758189251Ssam	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
759189251Ssam		   "Registrar");
760189251Ssam
761209158Srpaulo	if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
762209158Srpaulo	    0) {
763209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
764209158Srpaulo			   MACSTR ") does not match with own address (" MACSTR
765209158Srpaulo			   ")", MAC2STR(cred.mac_addr),
766209158Srpaulo			   MAC2STR(wps->wps->dev.mac_addr));
767209158Srpaulo		/*
768209158Srpaulo		 * In theory, this could be consider fatal error, but there are
769209158Srpaulo		 * number of deployed implementations using other address here
770209158Srpaulo		 * due to unclarity in the specification. For interoperability
771209158Srpaulo		 * reasons, allow this to be processed since we do not really
772209158Srpaulo		 * use the MAC Address information for anything.
773209158Srpaulo		 */
774252726Srpaulo#ifdef CONFIG_WPS_STRICT
775252726Srpaulo		if (wps2) {
776252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
777252726Srpaulo				   "MAC Address in AP Settings");
778252726Srpaulo			return -1;
779252726Srpaulo		}
780252726Srpaulo#endif /* CONFIG_WPS_STRICT */
781209158Srpaulo	}
782209158Srpaulo
783252726Srpaulo#ifdef CONFIG_WPS2
784252726Srpaulo	if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
785252726Srpaulo	{
786252726Srpaulo		if (cred.encr_type & WPS_ENCR_WEP) {
787252726Srpaulo			wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
788252726Srpaulo				   "due to WEP configuration");
789252726Srpaulo			wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
790252726Srpaulo			return -1;
791252726Srpaulo		}
792252726Srpaulo
793252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
794252726Srpaulo			   "invalid encr_type 0x%x", cred.encr_type);
795252726Srpaulo		return -1;
796252726Srpaulo	}
797252726Srpaulo#endif /* CONFIG_WPS2 */
798252726Srpaulo
799252726Srpaulo#ifdef CONFIG_WPS_STRICT
800252726Srpaulo	if (wps2) {
801252726Srpaulo		if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
802252726Srpaulo		    WPS_ENCR_TKIP ||
803252726Srpaulo		    (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
804252726Srpaulo		    WPS_AUTH_WPAPSK) {
805252726Srpaulo			wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
806252726Srpaulo				   "AP Settings: WPA-Personal/TKIP only");
807252726Srpaulo			wps->error_indication =
808252726Srpaulo				WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
809252726Srpaulo			return -1;
810252726Srpaulo		}
811252726Srpaulo	}
812252726Srpaulo#endif /* CONFIG_WPS_STRICT */
813252726Srpaulo
814252726Srpaulo#ifdef CONFIG_WPS2
815252726Srpaulo	if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
816252726Srpaulo	{
817252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
818252726Srpaulo			   "TKIP+AES");
819252726Srpaulo		cred.encr_type |= WPS_ENCR_AES;
820252726Srpaulo	}
821252726Srpaulo
822252726Srpaulo	if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
823252726Srpaulo	    WPS_AUTH_WPAPSK) {
824252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
825252726Srpaulo			   "WPAPSK+WPA2PSK");
826252726Srpaulo		cred.auth_type |= WPS_AUTH_WPA2PSK;
827252726Srpaulo	}
828252726Srpaulo#endif /* CONFIG_WPS2 */
829252726Srpaulo
830189251Ssam	if (wps->wps->cred_cb) {
831189251Ssam		cred.cred_attr = wpabuf_head(attrs);
832189251Ssam		cred.cred_attr_len = wpabuf_len(attrs);
833189251Ssam		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
834189251Ssam	}
835189251Ssam
836189251Ssam	return 0;
837189251Ssam}
838189251Ssam
839189251Ssam
840189251Ssamstatic enum wps_process_res wps_process_m2(struct wps_data *wps,
841189251Ssam					   const struct wpabuf *msg,
842189251Ssam					   struct wps_parse_attr *attr)
843189251Ssam{
844189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M2");
845189251Ssam
846189251Ssam	if (wps->state != RECV_M2) {
847189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
848189251Ssam			   "receiving M2", wps->state);
849189251Ssam		wps->state = SEND_WSC_NACK;
850189251Ssam		return WPS_CONTINUE;
851189251Ssam	}
852189251Ssam
853189251Ssam	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
854189251Ssam	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
855214734Srpaulo	    wps_process_uuid_r(wps, attr->uuid_r)) {
856189251Ssam		wps->state = SEND_WSC_NACK;
857189251Ssam		return WPS_CONTINUE;
858189251Ssam	}
859189251Ssam
860252726Srpaulo	/*
861252726Srpaulo	 * Stop here on an AP as an Enrollee if AP Setup is locked unless the
862252726Srpaulo	 * special locked mode is used to allow protocol run up to M7 in order
863252726Srpaulo	 * to support external Registrars that only learn the current AP
864252726Srpaulo	 * configuration without changing it.
865252726Srpaulo	 */
866214734Srpaulo	if (wps->wps->ap &&
867252726Srpaulo	    ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
868252726Srpaulo	     wps->dev_password == NULL)) {
869189251Ssam		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
870189251Ssam			   "registration of a new Registrar");
871189251Ssam		wps->config_error = WPS_CFG_SETUP_LOCKED;
872189251Ssam		wps->state = SEND_WSC_NACK;
873189251Ssam		return WPS_CONTINUE;
874189251Ssam	}
875189251Ssam
876214734Srpaulo	if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
877214734Srpaulo	    wps_process_authenticator(wps, attr->authenticator, msg) ||
878214734Srpaulo	    wps_process_device_attrs(&wps->peer_dev, attr)) {
879214734Srpaulo		wps->state = SEND_WSC_NACK;
880214734Srpaulo		return WPS_CONTINUE;
881214734Srpaulo	}
882214734Srpaulo
883189251Ssam	wps->state = SEND_M3;
884189251Ssam	return WPS_CONTINUE;
885189251Ssam}
886189251Ssam
887189251Ssam
888189251Ssamstatic enum wps_process_res wps_process_m2d(struct wps_data *wps,
889189251Ssam					    struct wps_parse_attr *attr)
890189251Ssam{
891189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
892189251Ssam
893189251Ssam	if (wps->state != RECV_M2) {
894189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
895189251Ssam			   "receiving M2D", wps->state);
896189251Ssam		wps->state = SEND_WSC_NACK;
897189251Ssam		return WPS_CONTINUE;
898189251Ssam	}
899189251Ssam
900189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
901189251Ssam			  attr->manufacturer, attr->manufacturer_len);
902189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
903189251Ssam			  attr->model_name, attr->model_name_len);
904189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
905189251Ssam			  attr->model_number, attr->model_number_len);
906189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
907189251Ssam			  attr->serial_number, attr->serial_number_len);
908189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
909189251Ssam			  attr->dev_name, attr->dev_name_len);
910189251Ssam
911189251Ssam	if (wps->wps->event_cb) {
912189251Ssam		union wps_event_data data;
913189251Ssam		struct wps_event_m2d *m2d = &data.m2d;
914189251Ssam		os_memset(&data, 0, sizeof(data));
915189251Ssam		if (attr->config_methods)
916189251Ssam			m2d->config_methods =
917189251Ssam				WPA_GET_BE16(attr->config_methods);
918189251Ssam		m2d->manufacturer = attr->manufacturer;
919189251Ssam		m2d->manufacturer_len = attr->manufacturer_len;
920189251Ssam		m2d->model_name = attr->model_name;
921189251Ssam		m2d->model_name_len = attr->model_name_len;
922189251Ssam		m2d->model_number = attr->model_number;
923189251Ssam		m2d->model_number_len = attr->model_number_len;
924189251Ssam		m2d->serial_number = attr->serial_number;
925189251Ssam		m2d->serial_number_len = attr->serial_number_len;
926189251Ssam		m2d->dev_name = attr->dev_name;
927189251Ssam		m2d->dev_name_len = attr->dev_name_len;
928189251Ssam		m2d->primary_dev_type = attr->primary_dev_type;
929189251Ssam		if (attr->config_error)
930189251Ssam			m2d->config_error =
931189251Ssam				WPA_GET_BE16(attr->config_error);
932189251Ssam		if (attr->dev_password_id)
933189251Ssam			m2d->dev_password_id =
934189251Ssam				WPA_GET_BE16(attr->dev_password_id);
935189251Ssam		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
936189251Ssam	}
937189251Ssam
938189251Ssam	wps->state = RECEIVED_M2D;
939189251Ssam	return WPS_CONTINUE;
940189251Ssam}
941189251Ssam
942189251Ssam
943189251Ssamstatic enum wps_process_res wps_process_m4(struct wps_data *wps,
944189251Ssam					   const struct wpabuf *msg,
945189251Ssam					   struct wps_parse_attr *attr)
946189251Ssam{
947189251Ssam	struct wpabuf *decrypted;
948189251Ssam	struct wps_parse_attr eattr;
949189251Ssam
950189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M4");
951189251Ssam
952189251Ssam	if (wps->state != RECV_M4) {
953189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
954189251Ssam			   "receiving M4", wps->state);
955189251Ssam		wps->state = SEND_WSC_NACK;
956189251Ssam		return WPS_CONTINUE;
957189251Ssam	}
958189251Ssam
959189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
960189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg) ||
961189251Ssam	    wps_process_r_hash1(wps, attr->r_hash1) ||
962189251Ssam	    wps_process_r_hash2(wps, attr->r_hash2)) {
963189251Ssam		wps->state = SEND_WSC_NACK;
964189251Ssam		return WPS_CONTINUE;
965189251Ssam	}
966189251Ssam
967189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
968189251Ssam					      attr->encr_settings_len);
969189251Ssam	if (decrypted == NULL) {
970189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
971189251Ssam			   "Settings attribute");
972189251Ssam		wps->state = SEND_WSC_NACK;
973189251Ssam		return WPS_CONTINUE;
974189251Ssam	}
975189251Ssam
976252726Srpaulo	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
977252726Srpaulo		wpabuf_free(decrypted);
978252726Srpaulo		wps->state = SEND_WSC_NACK;
979252726Srpaulo		return WPS_CONTINUE;
980252726Srpaulo	}
981252726Srpaulo
982189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
983189251Ssam		   "attribute");
984189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
985189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
986189251Ssam	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
987189251Ssam		wpabuf_free(decrypted);
988189251Ssam		wps->state = SEND_WSC_NACK;
989189251Ssam		return WPS_CONTINUE;
990189251Ssam	}
991189251Ssam	wpabuf_free(decrypted);
992189251Ssam
993189251Ssam	wps->state = SEND_M5;
994189251Ssam	return WPS_CONTINUE;
995189251Ssam}
996189251Ssam
997189251Ssam
998189251Ssamstatic enum wps_process_res wps_process_m6(struct wps_data *wps,
999189251Ssam					   const struct wpabuf *msg,
1000189251Ssam					   struct wps_parse_attr *attr)
1001189251Ssam{
1002189251Ssam	struct wpabuf *decrypted;
1003189251Ssam	struct wps_parse_attr eattr;
1004189251Ssam
1005189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M6");
1006189251Ssam
1007189251Ssam	if (wps->state != RECV_M6) {
1008189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1009189251Ssam			   "receiving M6", wps->state);
1010189251Ssam		wps->state = SEND_WSC_NACK;
1011189251Ssam		return WPS_CONTINUE;
1012189251Ssam	}
1013189251Ssam
1014189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1015189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg)) {
1016189251Ssam		wps->state = SEND_WSC_NACK;
1017189251Ssam		return WPS_CONTINUE;
1018189251Ssam	}
1019189251Ssam
1020189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1021189251Ssam					      attr->encr_settings_len);
1022189251Ssam	if (decrypted == NULL) {
1023189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1024189251Ssam			   "Settings attribute");
1025189251Ssam		wps->state = SEND_WSC_NACK;
1026189251Ssam		return WPS_CONTINUE;
1027189251Ssam	}
1028189251Ssam
1029252726Srpaulo	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
1030252726Srpaulo		wpabuf_free(decrypted);
1031252726Srpaulo		wps->state = SEND_WSC_NACK;
1032252726Srpaulo		return WPS_CONTINUE;
1033252726Srpaulo	}
1034252726Srpaulo
1035189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1036189251Ssam		   "attribute");
1037189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1038189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1039189251Ssam	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
1040189251Ssam		wpabuf_free(decrypted);
1041189251Ssam		wps->state = SEND_WSC_NACK;
1042189251Ssam		return WPS_CONTINUE;
1043189251Ssam	}
1044189251Ssam	wpabuf_free(decrypted);
1045189251Ssam
1046252726Srpaulo	if (wps->wps->ap)
1047252726Srpaulo		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
1048252726Srpaulo				   NULL);
1049252726Srpaulo
1050189251Ssam	wps->state = SEND_M7;
1051189251Ssam	return WPS_CONTINUE;
1052189251Ssam}
1053189251Ssam
1054189251Ssam
1055189251Ssamstatic enum wps_process_res wps_process_m8(struct wps_data *wps,
1056189251Ssam					   const struct wpabuf *msg,
1057189251Ssam					   struct wps_parse_attr *attr)
1058189251Ssam{
1059189251Ssam	struct wpabuf *decrypted;
1060189251Ssam	struct wps_parse_attr eattr;
1061189251Ssam
1062189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received M8");
1063189251Ssam
1064189251Ssam	if (wps->state != RECV_M8) {
1065189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
1066189251Ssam			   "receiving M8", wps->state);
1067189251Ssam		wps->state = SEND_WSC_NACK;
1068189251Ssam		return WPS_CONTINUE;
1069189251Ssam	}
1070189251Ssam
1071189251Ssam	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
1072189251Ssam	    wps_process_authenticator(wps, attr->authenticator, msg)) {
1073189251Ssam		wps->state = SEND_WSC_NACK;
1074189251Ssam		return WPS_CONTINUE;
1075189251Ssam	}
1076189251Ssam
1077252726Srpaulo	if (wps->wps->ap && wps->wps->ap_setup_locked) {
1078252726Srpaulo		/*
1079252726Srpaulo		 * Stop here if special ap_setup_locked == 2 mode allowed the
1080252726Srpaulo		 * protocol to continue beyond M2. This allows ER to learn the
1081252726Srpaulo		 * current AP settings without changing them.
1082252726Srpaulo		 */
1083252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
1084252726Srpaulo			   "registration of a new Registrar");
1085252726Srpaulo		wps->config_error = WPS_CFG_SETUP_LOCKED;
1086252726Srpaulo		wps->state = SEND_WSC_NACK;
1087252726Srpaulo		return WPS_CONTINUE;
1088252726Srpaulo	}
1089252726Srpaulo
1090189251Ssam	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
1091189251Ssam					      attr->encr_settings_len);
1092189251Ssam	if (decrypted == NULL) {
1093189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
1094189251Ssam			   "Settings attribute");
1095189251Ssam		wps->state = SEND_WSC_NACK;
1096189251Ssam		return WPS_CONTINUE;
1097189251Ssam	}
1098189251Ssam
1099252726Srpaulo	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
1100252726Srpaulo				 attr->version2 != NULL) < 0) {
1101252726Srpaulo		wpabuf_free(decrypted);
1102252726Srpaulo		wps->state = SEND_WSC_NACK;
1103252726Srpaulo		return WPS_CONTINUE;
1104252726Srpaulo	}
1105252726Srpaulo
1106189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
1107189251Ssam		   "attribute");
1108189251Ssam	if (wps_parse_msg(decrypted, &eattr) < 0 ||
1109189251Ssam	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
1110189251Ssam	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
1111252726Srpaulo			      eattr.num_cred, attr->version2 != NULL) ||
1112252726Srpaulo	    wps_process_ap_settings_e(wps, &eattr, decrypted,
1113252726Srpaulo				      attr->version2 != NULL)) {
1114189251Ssam		wpabuf_free(decrypted);
1115189251Ssam		wps->state = SEND_WSC_NACK;
1116189251Ssam		return WPS_CONTINUE;
1117189251Ssam	}
1118189251Ssam	wpabuf_free(decrypted);
1119189251Ssam
1120189251Ssam	wps->state = WPS_MSG_DONE;
1121189251Ssam	return WPS_CONTINUE;
1122189251Ssam}
1123189251Ssam
1124189251Ssam
1125189251Ssamstatic enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
1126189251Ssam						const struct wpabuf *msg)
1127189251Ssam{
1128189251Ssam	struct wps_parse_attr attr;
1129189251Ssam	enum wps_process_res ret = WPS_CONTINUE;
1130189251Ssam
1131189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
1132189251Ssam
1133189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1134189251Ssam		return WPS_FAILURE;
1135189251Ssam
1136189251Ssam	if (attr.enrollee_nonce == NULL ||
1137252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1138189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1139189251Ssam		return WPS_FAILURE;
1140189251Ssam	}
1141189251Ssam
1142189251Ssam	if (attr.msg_type == NULL) {
1143189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1144252726Srpaulo		wps->state = SEND_WSC_NACK;
1145252726Srpaulo		return WPS_CONTINUE;
1146189251Ssam	}
1147189251Ssam
1148189251Ssam	switch (*attr.msg_type) {
1149189251Ssam	case WPS_M2:
1150252726Srpaulo		if (wps_validate_m2(msg) < 0)
1151252726Srpaulo			return WPS_FAILURE;
1152189251Ssam		ret = wps_process_m2(wps, msg, &attr);
1153189251Ssam		break;
1154189251Ssam	case WPS_M2D:
1155252726Srpaulo		if (wps_validate_m2d(msg) < 0)
1156252726Srpaulo			return WPS_FAILURE;
1157189251Ssam		ret = wps_process_m2d(wps, &attr);
1158189251Ssam		break;
1159189251Ssam	case WPS_M4:
1160252726Srpaulo		if (wps_validate_m4(msg) < 0)
1161252726Srpaulo			return WPS_FAILURE;
1162189251Ssam		ret = wps_process_m4(wps, msg, &attr);
1163189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1164252726Srpaulo			wps_fail_event(wps->wps, WPS_M4, wps->config_error,
1165252726Srpaulo				       wps->error_indication);
1166189251Ssam		break;
1167189251Ssam	case WPS_M6:
1168252726Srpaulo		if (wps_validate_m6(msg) < 0)
1169252726Srpaulo			return WPS_FAILURE;
1170189251Ssam		ret = wps_process_m6(wps, msg, &attr);
1171189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1172252726Srpaulo			wps_fail_event(wps->wps, WPS_M6, wps->config_error,
1173252726Srpaulo				       wps->error_indication);
1174189251Ssam		break;
1175189251Ssam	case WPS_M8:
1176252726Srpaulo		if (wps_validate_m8(msg) < 0)
1177252726Srpaulo			return WPS_FAILURE;
1178189251Ssam		ret = wps_process_m8(wps, msg, &attr);
1179189251Ssam		if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
1180252726Srpaulo			wps_fail_event(wps->wps, WPS_M8, wps->config_error,
1181252726Srpaulo				       wps->error_indication);
1182189251Ssam		break;
1183189251Ssam	default:
1184189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
1185189251Ssam			   *attr.msg_type);
1186189251Ssam		return WPS_FAILURE;
1187189251Ssam	}
1188189251Ssam
1189189251Ssam	/*
1190189251Ssam	 * Save a copy of the last message for Authenticator derivation if we
1191189251Ssam	 * are continuing. However, skip M2D since it is not authenticated and
1192189251Ssam	 * neither is the ACK/NACK response frame. This allows the possibly
1193189251Ssam	 * following M2 to be processed correctly by using the previously sent
1194189251Ssam	 * M1 in Authenticator derivation.
1195189251Ssam	 */
1196189251Ssam	if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
1197189251Ssam		/* Save a copy of the last message for Authenticator derivation
1198189251Ssam		 */
1199189251Ssam		wpabuf_free(wps->last_msg);
1200189251Ssam		wps->last_msg = wpabuf_dup(msg);
1201189251Ssam	}
1202189251Ssam
1203189251Ssam	return ret;
1204189251Ssam}
1205189251Ssam
1206189251Ssam
1207189251Ssamstatic enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
1208189251Ssam						const struct wpabuf *msg)
1209189251Ssam{
1210189251Ssam	struct wps_parse_attr attr;
1211189251Ssam
1212189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
1213189251Ssam
1214189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1215189251Ssam		return WPS_FAILURE;
1216189251Ssam
1217189251Ssam	if (attr.msg_type == NULL) {
1218189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1219189251Ssam		return WPS_FAILURE;
1220189251Ssam	}
1221189251Ssam
1222189251Ssam	if (*attr.msg_type != WPS_WSC_ACK) {
1223189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1224189251Ssam			   *attr.msg_type);
1225189251Ssam		return WPS_FAILURE;
1226189251Ssam	}
1227189251Ssam
1228189251Ssam	if (attr.registrar_nonce == NULL ||
1229252726Srpaulo	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1230189251Ssam	{
1231189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1232189251Ssam		return WPS_FAILURE;
1233189251Ssam	}
1234189251Ssam
1235189251Ssam	if (attr.enrollee_nonce == NULL ||
1236252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1237189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1238189251Ssam		return WPS_FAILURE;
1239189251Ssam	}
1240189251Ssam
1241189251Ssam	if (wps->state == RECV_ACK && wps->wps->ap) {
1242189251Ssam		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
1243189251Ssam			   "completed successfully");
1244189251Ssam		wps_success_event(wps->wps);
1245189251Ssam		wps->state = WPS_FINISHED;
1246189251Ssam		return WPS_DONE;
1247189251Ssam	}
1248189251Ssam
1249189251Ssam	return WPS_FAILURE;
1250189251Ssam}
1251189251Ssam
1252189251Ssam
1253189251Ssamstatic enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
1254189251Ssam						 const struct wpabuf *msg)
1255189251Ssam{
1256189251Ssam	struct wps_parse_attr attr;
1257252726Srpaulo	u16 config_error;
1258189251Ssam
1259189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
1260189251Ssam
1261189251Ssam	if (wps_parse_msg(msg, &attr) < 0)
1262189251Ssam		return WPS_FAILURE;
1263189251Ssam
1264189251Ssam	if (attr.msg_type == NULL) {
1265189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
1266189251Ssam		return WPS_FAILURE;
1267189251Ssam	}
1268189251Ssam
1269189251Ssam	if (*attr.msg_type != WPS_WSC_NACK) {
1270189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
1271189251Ssam			   *attr.msg_type);
1272189251Ssam		return WPS_FAILURE;
1273189251Ssam	}
1274189251Ssam
1275189251Ssam	if (attr.registrar_nonce == NULL ||
1276252726Srpaulo	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
1277189251Ssam	{
1278189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
1279189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
1280189251Ssam			    attr.registrar_nonce, WPS_NONCE_LEN);
1281189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
1282189251Ssam			    wps->nonce_r, WPS_NONCE_LEN);
1283189251Ssam		return WPS_FAILURE;
1284189251Ssam	}
1285189251Ssam
1286189251Ssam	if (attr.enrollee_nonce == NULL ||
1287252726Srpaulo	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
1288189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
1289189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
1290189251Ssam			    attr.enrollee_nonce, WPS_NONCE_LEN);
1291189251Ssam		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
1292189251Ssam			    wps->nonce_e, WPS_NONCE_LEN);
1293189251Ssam		return WPS_FAILURE;
1294189251Ssam	}
1295189251Ssam
1296189251Ssam	if (attr.config_error == NULL) {
1297189251Ssam		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
1298189251Ssam			   "in WSC_NACK");
1299189251Ssam		return WPS_FAILURE;
1300189251Ssam	}
1301189251Ssam
1302252726Srpaulo	config_error = WPA_GET_BE16(attr.config_error);
1303189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
1304252726Srpaulo		   "Configuration Error %d", config_error);
1305189251Ssam
1306189251Ssam	switch (wps->state) {
1307189251Ssam	case RECV_M4:
1308252726Srpaulo		wps_fail_event(wps->wps, WPS_M3, config_error,
1309252726Srpaulo			       wps->error_indication);
1310189251Ssam		break;
1311189251Ssam	case RECV_M6:
1312252726Srpaulo		wps_fail_event(wps->wps, WPS_M5, config_error,
1313252726Srpaulo			       wps->error_indication);
1314189251Ssam		break;
1315189251Ssam	case RECV_M8:
1316252726Srpaulo		wps_fail_event(wps->wps, WPS_M7, config_error,
1317252726Srpaulo			       wps->error_indication);
1318189251Ssam		break;
1319189251Ssam	default:
1320189251Ssam		break;
1321189251Ssam	}
1322189251Ssam
1323189251Ssam	/* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
1324189251Ssam	 * Enrollee is Authenticator */
1325189251Ssam	wps->state = SEND_WSC_NACK;
1326189251Ssam
1327189251Ssam	return WPS_FAILURE;
1328189251Ssam}
1329189251Ssam
1330189251Ssam
1331189251Ssamenum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
1332189251Ssam					      enum wsc_op_code op_code,
1333189251Ssam					      const struct wpabuf *msg)
1334189251Ssam{
1335189251Ssam
1336189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
1337189251Ssam		   "op_code=%d)",
1338189251Ssam		   (unsigned long) wpabuf_len(msg), op_code);
1339189251Ssam
1340209158Srpaulo	if (op_code == WSC_UPnP) {
1341209158Srpaulo		/* Determine the OpCode based on message type attribute */
1342209158Srpaulo		struct wps_parse_attr attr;
1343209158Srpaulo		if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
1344209158Srpaulo			if (*attr.msg_type == WPS_WSC_ACK)
1345209158Srpaulo				op_code = WSC_ACK;
1346209158Srpaulo			else if (*attr.msg_type == WPS_WSC_NACK)
1347209158Srpaulo				op_code = WSC_NACK;
1348209158Srpaulo		}
1349209158Srpaulo	}
1350209158Srpaulo
1351189251Ssam	switch (op_code) {
1352189251Ssam	case WSC_MSG:
1353189251Ssam	case WSC_UPnP:
1354189251Ssam		return wps_process_wsc_msg(wps, msg);
1355189251Ssam	case WSC_ACK:
1356252726Srpaulo		if (wps_validate_wsc_ack(msg) < 0)
1357252726Srpaulo			return WPS_FAILURE;
1358189251Ssam		return wps_process_wsc_ack(wps, msg);
1359189251Ssam	case WSC_NACK:
1360252726Srpaulo		if (wps_validate_wsc_nack(msg) < 0)
1361252726Srpaulo			return WPS_FAILURE;
1362189251Ssam		return wps_process_wsc_nack(wps, msg);
1363189251Ssam	default:
1364189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
1365189251Ssam		return WPS_FAILURE;
1366189251Ssam	}
1367189251Ssam}
1368