diff -urN wpa_supplicant-0.5.10.orig/config.c wpa_supplicant-0.5.10/config.c
--- wpa_supplicant-0.5.10.orig/config.c	2008-02-17 16:27:56.000000000 -0800
+++ wpa_supplicant-0.5.10/config.c	2008-08-13 11:00:41.000000000 -0700
@@ -378,6 +378,8 @@
 		else if (os_strcmp(start, "RSN") == 0 ||
 			 os_strcmp(start, "WPA2") == 0)
 			val |= WPA_PROTO_RSN;
+		else if (os_strcmp(start, "WPS") == 0)
+			val |= WPA_PROTO_WPS;
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
 				   line, start);
@@ -429,6 +431,14 @@
 		first = 0;
 	}
 
+	if (ssid->proto & WPA_PROTO_WPS) {
+		ret = os_snprintf(pos, end - pos, "%sWPS", first ? "" : " ");
+		if (ret < 0 || ret >= end - pos)
+			return buf;
+		pos += ret;
+		first = 0;
+	}
+
 	return buf;
 }
 
@@ -1139,6 +1149,9 @@
 	{ STR(key_id) },
 	{ INT(engine) },
 	{ INT(eapol_flags) },
+#ifdef EAP_WPS
+	{ STR(uuid_e) },
+#endif
 #endif /* IEEE8021X_EAPOL */
 	{ FUNC_KEY(wep_key0) },
 	{ FUNC_KEY(wep_key1) },
@@ -1305,6 +1318,10 @@
 	os_free(ssid->pin);
 	os_free(ssid->engine_id);
 	os_free(ssid->key_id);
+#ifdef EAP_WPS
+	os_free(ssid->uuid_e);
+	os_free(ssid->wps_pin);
+#endif
 	os_free(ssid->otp);
 	os_free(ssid->pending_req_otp);
 	os_free(ssid->pac_file);
diff -urN wpa_supplicant-0.5.10.orig/config_file.c wpa_supplicant-0.5.10/config_file.c
--- wpa_supplicant-0.5.10.orig/config_file.c	2007-03-24 19:09:49.000000000 -0700
+++ wpa_supplicant-0.5.10/config_file.c	2008-07-16 15:27:06.000000000 -0700
@@ -104,7 +104,13 @@
 		wpa_config_update_psk(ssid);
 	}
 
-	if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) {
+	if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) &&
+			!ssid->psk_set
+#ifdef EAP_WPS
+			/* PSK not set with WPS */
+			&& !(ssid->proto & WPA_PROTO_WPS)
+#endif
+		) {
 		wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key "
 			   "management, but no PSK configured.", line);
 		errors++;
diff -urN wpa_supplicant-0.5.10.orig/config_ssid.h wpa_supplicant-0.5.10/config_ssid.h
--- wpa_supplicant-0.5.10.orig/config_ssid.h	2007-12-27 15:59:35.000000000 -0800
+++ wpa_supplicant-0.5.10/config_ssid.h	2008-09-03 09:45:20.000000000 -0700
@@ -36,6 +36,7 @@
 
 #define WPA_PROTO_WPA BIT(0)
 #define WPA_PROTO_RSN BIT(1)
+#define WPA_PROTO_WPS BIT(2)
 
 #define WPA_AUTH_ALG_OPEN BIT(0)
 #define WPA_AUTH_ALG_SHARED BIT(1)
@@ -46,7 +47,6 @@
 #define EAP_PSK_LEN_MIN 16
 #define EAP_PSK_LEN_MAX 32
 
-
 #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
 #define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
 			     EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
@@ -590,6 +590,38 @@
 	 */
 	int eapol_flags;
 
+	/**
+	 * uuid_e - The Universally Unique ID for the enrollee
+	 *
+	 * This is supplied by the user. The format should be
+	 * 16 hex digits in ASCII format.
+	 *
+	 */
+	char *uuid_e;
+
+	/**
+	 * wps_pin - The WPS PIN number
+	 *
+	 * This is created by the supplicant
+	 */
+	char *wps_pin;
+
+	/**
+	 * own_addr - The MAC address of this device
+	 *
+	 * This field should not be set in the configuration step. It is only
+	 * used internally in the construction of WPS messages.
+	 */
+	u8 own_addr[ETH_ALEN];
+
+	/**
+	 * assoc_state - The WPS association state
+	 *
+	 * This field should not be set in the configuration step. It is only
+	 * used internally.
+	 */
+	u16 assoc_state;
+
 #endif /* IEEE8021X_EAPOL */
 
 #define NUM_WEP_KEYS 4
diff -urN wpa_supplicant-0.5.10.orig/crypto_internal.c wpa_supplicant-0.5.10/crypto_internal.c
--- wpa_supplicant-0.5.10.orig/crypto_internal.c	2007-03-24 19:09:49.000000000 -0700
+++ wpa_supplicant-0.5.10/crypto_internal.c	2008-07-31 15:13:16.000000000 -0700
@@ -625,7 +625,7 @@
 }
 
 
-#ifdef EAP_FAST
+#if defined(EAP_FAST) || defined(EAP_WPS)
 
 int crypto_mod_exp(const u8 *base, size_t base_len,
 		   const u8 *power, size_t power_len,
diff -urN wpa_supplicant-0.5.10.orig/ctrl_iface.c wpa_supplicant-0.5.10/ctrl_iface.c
--- wpa_supplicant-0.5.10.orig/ctrl_iface.c	2007-11-27 20:55:36.000000000 -0800
+++ wpa_supplicant-0.5.10/ctrl_iface.c	2008-07-24 10:17:50.000000000 -0700
@@ -17,10 +17,12 @@
 #include "common.h"
 #include "eloop.h"
 #include "wpa.h"
+#include "wps.h"
 #include "wpa_supplicant.h"
 #include "config.h"
 #include "eapol_sm.h"
 #include "wpa_supplicant_i.h"
+#include "wps_i.h"
 #include "ctrl_iface.h"
 #include "l2_packet.h"
 #include "preauth.h"
@@ -470,6 +472,32 @@
 	return pos;
 }
 
+#ifdef EAP_WPS
+static char * wps_ie_txt(char *pos, char *end, const char *proto,
+				    const u8 *ie, size_t ie_len)
+{
+	struct wps_ie_data data;
+	int first, ret;
+
+	ret = os_snprintf(pos, end - pos, "[%s-", proto);
+	if (ret < 0 || ret >= end - pos)
+		return pos;
+	pos += ret;
+
+	if (wpa_parse_wps_ie(ie, ie_len, &data) < 0) {
+		ret = os_snprintf(pos, end - pos, "?]");
+		if (ret < 0 || ret >= end - pos)
+			return pos;
+		pos += ret;
+		return pos;
+	}
+
+	first = 1;
+
+	return pos;
+}
+#endif
+
 
 static int wpa_supplicant_ctrl_iface_scan_results(
 	struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
@@ -509,7 +537,14 @@
 						    res->rsn_ie,
 						    res->rsn_ie_len);
 		}
-		if (!res->wpa_ie_len && !res->rsn_ie_len &&
+#ifdef EAP_WPS
+		if (res->wps_ie_len) {
+			pos = wps_ie_txt(pos, end, "WPS",
+							res->wps_ie,
+							res->wps_ie_len);
+		}
+#endif
+		if (!res->wpa_ie_len && !res->rsn_ie_len && !res->wps_ie_len &&
 		    res->caps & IEEE80211_CAP_PRIVACY) {
 			ret = os_snprintf(pos, end - pos, "[WEP]");
 			if (ret < 0 || ret >= end - pos)
@@ -1195,6 +1230,14 @@
 	} else if (os_strcmp(buf, "INTERFACES") == 0) {
 		reply_len = wpa_supplicant_global_iface_interfaces(
 			wpa_s->global, reply, reply_size);
+#ifdef EAP_WPS
+	} else if (os_strcmp(buf, "PBC") == 0) {
+		wps_config_pbc(wpa_s);
+	} else if (os_strcmp(buf, "PIN_GET") == 0) {
+		reply_len = wps_get_pin(wpa_s, reply, reply_size);
+	} else if (os_strcmp(buf, "PIN_ENTERED") == 0) {
+		wps_config_pin(wpa_s);
+#endif
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff -urN wpa_supplicant-0.5.10.orig/defconfig wpa_supplicant-0.5.10/defconfig
--- wpa_supplicant-0.5.10.orig/defconfig	2007-12-08 20:20:43.000000000 -0800
+++ wpa_supplicant-0.5.10/defconfig	2008-09-03 10:24:13.000000000 -0700
@@ -158,6 +158,13 @@
 # Include support for optional SHA256 cipher suite in EAP-GPSK
 #CONFIG_EAP_GPSK_SHA256=y
 
+# EAP-WPS (enable Wifi Protected Setup, sometimes refered to as Simple Config)
+# This EAP method requires CONFIG_IEEE8021X_EAPOL as well as
+# CONFIG_TLS=internal
+#CONFIG_EAP_WPS=y
+#CONFIG_TLS=internal
+#CONFIG_INTERNAL_LIBTOMMATH=y
+
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 CONFIG_PKCS12=y
diff -urN wpa_supplicant-0.5.10.orig/driver_bsd.c wpa_supplicant-0.5.10/driver_bsd.c
--- wpa_supplicant-0.5.10.orig/driver_bsd.c	2006-09-11 19:33:20.000000000 -0700
+++ wpa_supplicant-0.5.10/driver_bsd.c	2008-07-29 14:25:40.000000000 -0700
@@ -608,6 +608,12 @@
 	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
 }
 
+static int __inline
+iswpsoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPS_OUI_TYPE<<24)|WPA_OUI);
+}
+
 static int
 wpa_driver_bsd_get_scan_results(void *priv,
 				     struct wpa_scan_result *results,
@@ -646,12 +652,17 @@
 			while (ielen > 0) {
 				switch (vp[0]) {
 				case IEEE80211_ELEMID_VENDOR:
-					if (!iswpaoui(vp))
-						break;
-					wsr->wpa_ie_len =
-					    min(2+vp[1], SSID_MAX_WPA_IE_LEN);
-					os_memcpy(wsr->wpa_ie, vp,
-						  wsr->wpa_ie_len);
+					if (iswpaoui(vp)) {
+						wsr->wpa_ie_len =
+							min(2+vp[1], SSID_MAX_WPA_IE_LEN);
+						os_memcpy(wsr->wpa_ie, vp,
+							  wsr->wpa_ie_len);
+					} else if (iswpsoui(vp)) {
+						wsr->wps_ie_len =
+							min(2+vp[1], SSID_MAX_WPS_IE_LEN);
+						os_memcpy(wsr->wps_ie, vp,
+								wsr->wps_ie_len);
+					}
 					break;
 				case IEEE80211_ELEMID_RSN:
 					wsr->rsn_ie_len =
diff -urN wpa_supplicant-0.5.10.orig/driver.h wpa_supplicant-0.5.10/driver.h
--- wpa_supplicant-0.5.10.orig/driver.h	2007-03-24 19:09:49.000000000 -0700
+++ wpa_supplicant-0.5.10/driver.h	2008-08-15 11:50:28.000000000 -0700
@@ -31,6 +31,7 @@
 #define IEEE80211_CAP_PRIVACY	0x0010
 
 #define SSID_MAX_WPA_IE_LEN 40
+#define SSID_MAX_WPS_IE_LEN 257
 /**
  * struct wpa_scan_result - Scan results
  * @bssid: BSSID
@@ -40,6 +41,8 @@
  * @wpa_ie_len: length of the wpa_ie
  * @rsn_ie: RSN IE
  * @rsn_ie_len: length of the RSN IE
+ * @wps_ie: WPS IE
+ * @wps_ie_len: length of the WPS IE
  * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
  * @caps: capability information field in host byte order
  * @qual: signal quality
@@ -59,6 +62,8 @@
 	size_t wpa_ie_len;
 	u8 rsn_ie[SSID_MAX_WPA_IE_LEN];
 	size_t rsn_ie_len;
+	u8 wps_ie[SSID_MAX_WPS_IE_LEN];
+	size_t wps_ie_len;
 	int freq;
 	u16 caps;
 	int qual;
@@ -145,6 +150,15 @@
 	int wep_tx_keyidx;
 
 	/**
+	 * wps_ie - WPS information element for Association/Probe Request
+	 */
+	const u8 *wps_ie;
+	/**
+	 * wps_ie_len - length of wps_ie
+	 */
+	size_t wps_ie_len;
+
+	/**
 	 * mgmt_frame_protection - IEEE 802.11w management frame protection
 	 */
 	enum {
@@ -752,6 +766,15 @@
 	 * (management frame processing) to wpa_supplicant.
 	 */
 	 int (*mlme_remove_sta)(void *priv, const u8 *addr);
+
+	/**
+	 * * set_probe_req_ie - Set information element(s) for Probe Request
+	 * @priv: private driver interface data
+	 * @ies: Information elements to append or %NULL to remove extra IEs
+	 * @ies_len: Length of the IE buffer in octets
+	 * Returns: 0 on success, -1 on failure
+	 */
+	 int (*set_probe_req_ie)(void *priv, u8 *ies, size_t ies_len);
 };
 
 #endif /* DRIVER_H */
diff -urN wpa_supplicant-0.5.10.orig/driver_wext.c wpa_supplicant-0.5.10/driver_wext.c
--- wpa_supplicant-0.5.10.orig/driver_wext.c	2008-02-17 10:38:29.000000000 -0800
+++ wpa_supplicant-0.5.10/driver_wext.c	2008-08-18 15:42:58.000000000 -0700
@@ -1302,20 +1302,33 @@
 			while (gpos + 1 < gend &&
 			       gpos + 2 + (u8) gpos[1] <= gend) {
 				u8 ie = gpos[0], ielen = gpos[1] + 2;
+				/* XXX cft - removed check because WPS IE length (7-257) can be
+				 * much bigger than the WPA IE length (40)
+				 */
+#if 0
 				if (ielen > SSID_MAX_WPA_IE_LEN) {
 					gpos += ielen;
 					continue;
 				}
+#endif
 				switch (ie) {
 				case GENERIC_INFO_ELEM:
-					if (ielen < 2 + 4 ||
-					    os_memcmp(&gpos[2],
-						      "\x00\x50\xf2\x01", 4) !=
-					    0)
+					if (ielen < 2 + 4)
 						break;
-					os_memcpy(results[ap_num].wpa_ie, gpos,
-						  ielen);
-					results[ap_num].wpa_ie_len = ielen;
+					else if (os_memcmp(&gpos[2],
+						      "\x00\x50\xf2\x01", 4) == 0) {
+						os_memcpy(results[ap_num].wpa_ie, gpos,
+							  ielen);
+						results[ap_num].wpa_ie_len = ielen;
+					}
+#ifdef EAP_WPS
+					else if (os_memcmp(&gpos[2],
+						      "\x00\x50\xf2\x04", 4) == 0) {
+						os_memcpy(results[ap_num].wps_ie, gpos,
+							  ielen);
+						results[ap_num].wps_ie_len = ielen;
+					}
+#endif
 					break;
 				case RSN_INFO_ELEM:
 					os_memcpy(results[ap_num].rsn_ie, gpos,
@@ -1830,9 +1843,15 @@
 	/* TODO: should consider getting wpa version and cipher/key_mgmt suites
 	 * from configuration, not from here, where only the selected suite is
 	 * available */
-	if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
-	    < 0)
-		ret = -1;
+	if (0 != params->wps_ie_len) {
+		if (wpa_driver_wext_set_gen_ie(drv, params->wps_ie, params->wps_ie_len)
+			< 0)
+			ret = -1;
+	} else {
+		if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
+			< 0)
+			ret = -1;
+	}
 	if (params->wpa_ie == NULL || params->wpa_ie_len == 0)
 		value = IW_AUTH_WPA_VERSION_DISABLED;
 	else if (params->wpa_ie[0] == RSN_INFO_ELEM)
@@ -2327,6 +2346,11 @@
 	return 0;
 }
 
+int wpa_driver_wext_set_ie(void *priv, u8 *ie, size_t ie_len)
+{
+	return wpa_driver_wext_set_gen_ie(priv, ie, ie_len);
+}
+
 
 int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
 {
@@ -2366,4 +2390,5 @@
 	.mlme_add_sta = wpa_driver_wext_mlme_add_sta,
 	.mlme_remove_sta = wpa_driver_wext_mlme_remove_sta,
 #endif /* CONFIG_CLIENT_MLME */
+	.set_probe_req_ie = wpa_driver_wext_set_ie,
 };
diff -urN wpa_supplicant-0.5.10.orig/eap.c wpa_supplicant-0.5.10/eap.c
--- wpa_supplicant-0.5.10.orig/eap.c	2007-03-24 19:09:50.000000000 -0700
+++ wpa_supplicant-0.5.10/eap.c	2008-10-23 15:44:36.334923000 -0700
@@ -665,7 +665,11 @@
 			SM_ENTER(EAP, SEND_RESPONSE);
 		break;
 	case EAP_SEND_RESPONSE:
-		SM_ENTER(EAP, IDLE);
+		if (sm->decision == DECISION_FAIL && sm->methodState ==
+				METHOD_DONE)
+			SM_ENTER(EAP, FAILURE);
+		else
+			SM_ENTER(EAP, IDLE);
 		break;
 	case EAP_DISCARD:
 		SM_ENTER(EAP, IDLE);
diff -urN wpa_supplicant-0.5.10.orig/eap_defs.h wpa_supplicant-0.5.10/eap_defs.h
--- wpa_supplicant-0.5.10.orig/eap_defs.h	2007-03-24 19:09:50.000000000 -0700
+++ wpa_supplicant-0.5.10/eap_defs.h	2008-06-21 13:42:20.000000000 -0700
@@ -41,6 +41,7 @@
 typedef enum {
 	EAP_TYPE_NONE = 0,
 	EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+	EAP_TYPE_SIMPLE_CONFIG = 1,
 	EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
 	EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
 	EAP_TYPE_MD5 = 4, /* RFC 3748 */
@@ -66,9 +67,12 @@
 
 /* SMI Network Management Private Enterprise Code for vendor specific types */
 enum {
-	EAP_VENDOR_IETF = 0
+	EAP_VENDOR_IETF = 0,
+	EAP_VENDOR_WFA  = 14122
 };
 
+#define WPS_EAP_IDENTITY "WFA-SimpleConfig-Enrollee-1-0"
+
 #define EAP_MSK_LEN 64
 #define EAP_EMSK_LEN 64
 
diff -urN wpa_supplicant-0.5.10.orig/eap_methods.c wpa_supplicant-0.5.10/eap_methods.c
--- wpa_supplicant-0.5.10.orig/eap_methods.c	2007-03-24 19:09:50.000000000 -0700
+++ wpa_supplicant-0.5.10/eap_methods.c	2008-06-20 15:18:08.000000000 -0700
@@ -462,6 +462,13 @@
 	}
 #endif /* EAP_VENDOR_TEST */
 
+#ifdef EAP_WPS
+	if (ret == 0) {
+		int eap_peer_wps_register(void);
+		ret = eap_peer_wps_register();
+	}
+#endif
+
 	return ret;
 }
 
diff -urN wpa_supplicant-0.5.10.orig/eapol_sm.c wpa_supplicant-0.5.10/eapol_sm.c
--- wpa_supplicant-0.5.10.orig/eapol_sm.c	2007-12-27 16:33:26.000000000 -0800
+++ wpa_supplicant-0.5.10/eapol_sm.c	2008-09-03 08:53:05.000000000 -0700
@@ -381,6 +381,11 @@
 			if (sm->ctx->eapol_done_cb)
 				sm->ctx->eapol_done_cb(sm->ctx->ctx);
 		}
+		if (sm->eapFail && sm->conf.expect_fail) {
+			if (sm->ctx->eapol_done_cb)
+				sm->ctx->eapol_done_cb(sm->ctx->ctx);
+			SM_ENTER(SUPP_PAE, HELD);
+		}
 		if (sm->eapSuccess && sm->portValid)
 			SM_ENTER(SUPP_PAE, AUTHENTICATED);
 		else if (sm->eapFail || (sm->keyDone && !sm->portValid))
@@ -1331,6 +1336,7 @@
 	sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
 	sm->conf.required_keys = conf->required_keys;
 	sm->conf.fast_reauth = conf->fast_reauth;
+	sm->conf.expect_fail = conf->expect_fail;
 	if (sm->eap) {
 		eap_set_fast_reauth(sm->eap, conf->fast_reauth);
 		eap_set_workaround(sm->eap, conf->workaround);
diff -urN wpa_supplicant-0.5.10.orig/eapol_sm.h wpa_supplicant-0.5.10/eapol_sm.h
--- wpa_supplicant-0.5.10.orig/eapol_sm.h	2007-03-24 19:09:50.000000000 -0700
+++ wpa_supplicant-0.5.10/eapol_sm.h	2008-08-08 15:10:58.000000000 -0700
@@ -59,6 +59,13 @@
 	 * eap_disabled - Whether EAP is disabled
 	 */
 	int eap_disabled;
+
+	/**
+	 * expect_fail - Whether EAP-Failure is actually an indication of success
+	 *
+	 * This is only used for WPS
+	 */
+	int expect_fail;
 };
 
 struct eapol_sm;
diff -urN wpa_supplicant-0.5.10.orig/eap_wps.c wpa_supplicant-0.5.10/eap_wps.c
--- wpa_supplicant-0.5.10.orig/eap_wps.c	1969-12-31 16:00:00.000000000 -0800
+++ wpa_supplicant-0.5.10/eap_wps.c	2008-12-03 10:02:36.282416000 -0800
@@ -0,0 +1,1815 @@
+/*
+ * EAP peer method: EAP-WPS
+ * Copyright (c) 2008, Chuck Tuffli <chuck@tuffli.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "config.h"
+#include "config_ssid.h"
+#include "sha256.h"
+#include "aes_wrap.h"
+#include "crypto.h"
+#include "wps.h"
+#include "wpa_ctrl.h"
+
+/**
+ * WPS_PARSE_ERR - Incoming message did not parse correctly
+ */
+#define WPS_PARSE_ERR			-1
+
+/**
+ * WPS_AUTHENTICATOR_ERR - Incoming message's authenticator invalid
+ */
+#define WPS_AUTHENTICATOR_ERR	-2
+
+/**
+ * WPS_M2D_ERR - Received a M2D message instead of M2
+ */
+#define WPS_M2D_ERR		-3
+
+/**
+ * WPS_NONCE_LEN - Byte length of 128-bit random number (nonce)
+ */
+#define WPS_NONCE_LEN			(128/8)
+
+/**
+ * EAP_WPS_KDK_LEN - Byte length of 256-bit HMAC-SHA-256 Key Derivation Key
+ */
+#define EAP_WPS_KDK_LEN			(256/8)
+
+/**
+ * WPS_AUTHKEY_LEN - AuthKey byte length (256-bits)
+ */
+#define WPS_AUTHKEY_LEN			(256/8)
+
+/**
+ * WPS_KEYWRAPKEY_LEN - KeyWrapKey byte length (128-bits)
+ */
+#define WPS_KEYWRAPKEY_LEN		(128/8)
+
+/**
+ * WPS_EMSK_LEN - EMSK byte length (256-bits)
+ */
+#define WPS_EMSK_LEN			(256/8)
+
+/**
+ * WPS_S_NONCE_LEN - Secret nonces (ie E-S1, E-S2, R-S1, R-S2) byte length (128-bits)
+ */
+#define WPS_S_NONCE_LEN			(128/8)
+
+/**
+ * WPS_AUTHENTICATOR_LEN - Authenticator element byte length (64-bits)
+ */
+#define WPS_AUTHENTICATOR_LEN	(64/8)
+
+/**
+ * WPS_IV_LEN - Initialization Vector Length (128-bits)
+ */
+#define WPS_IV_LEN				(128/8)
+
+/**
+ * WPS_KWA_LEN - Key Wrap Authenticator Length (64-bits)
+ */
+#define WPS_KWA_LEN				(64/8)
+
+/**
+ * WPS_ENCRYPTED_SETTINGS_LEN - Encrypted Settings Length
+ */
+#define WPS_ENCRYPTED_SETTINGS_LEN 64
+
+/**
+ * WPS_PK_LEN - Enrollee/Registrar's Public Key length (1536-bits)
+ */
+#define WPS_PK_LEN				(1536/8)
+
+/**
+ * WPS_MSG_BUF_LEN - Default allocation size of the message buffer
+ */
+#define WPS_MSG_BUF_LEN			512
+
+#define WPS_WPAPSK_PASSPHRASE_LEN 64
+
+#define WPS_TLV_LEN				4
+#define WPS_TLV_GET_TYPE(p)		WPA_GET_BE16(p)
+#define WPS_TLV_GET_LEN(p)		WPA_GET_BE16(((u8 *)(p)) + 2)
+#define WPS_TLV_GET_VAL(p)		(((u8 *)(p)) + WPS_TLV_LEN)
+
+
+/**
+ * MODP - The 1536 bit MODP group used by WPS (see RFC 3526)
+ */
+static const u8 wps_dh_modp[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
+	0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
+	0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
+	0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22,
+	0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
+	0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b,
+	0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
+	0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
+	0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
+	0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b,
+	0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed,
+	0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5,
+	0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6,
+	0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d,
+	0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05,
+	0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a,
+	0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f,
+	0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96,
+	0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb,
+	0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d,
+	0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04,
+	0xf1, 0x74, 0x6c, 0x08, 0xca, 0x23, 0x73, 0x27,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+static const char *config_err_str[] = {
+	"No Error",
+	"OOB Interface Read Error",
+	"Decryption CRC Failure",
+	"2.4 channel not supported",
+	"5.0 channel not supported",
+	"Signal too weak",
+	"Network auth failure",
+	"Network association failure",
+	"No DHCP response",
+	"Failed DHCP config",
+	"IP address conflict",
+	"Couldn’t connect to Registrar",
+	"Multiple PBC sessions detected",
+	"Rogue activity suspected",
+	"Device busy",
+	"Setup locked",
+	"Message Timeout",
+	"Registration Session Timeout",
+	"Device Password Auth Failure"
+};
+
+static const size_t config_err_str_len = sizeof(config_err_str)/sizeof(char *);
+
+static const size_t wps_dh_modp_size = sizeof(wps_dh_modp);
+
+
+static const u8 wps_dh_generator = 2;
+
+
+struct eap_wps_data {
+	enum { WPS_START, WPS_M2, WPS_M4, WPS_M6, WPS_M8 } state;
+	
+	/* Enrollee's nonce */
+	u8 N1[WPS_NONCE_LEN];
+	
+	/* Enrollee's Diffie-Hellman private (secret) key */
+	u8 SKE[WPS_PK_LEN];
+
+	/* Enrollee's Diffie-Hellman public key */
+	u8 PKE[WPS_PK_LEN];
+
+	/* Enrollee's PSK values derived from Device Password */
+	u8 PSK1[SHA256_MAC_LEN];
+	u8 PSK2[SHA256_MAC_LEN];
+
+	/* Enrollee's secret nonces */
+	u8 ES1[WPS_S_NONCE_LEN];
+	u8 ES2[WPS_S_NONCE_LEN];
+
+	/* Registrar's nonce */
+	u8 N2[WPS_NONCE_LEN];
+	
+	/* Registrar's Diffie-Hellman public key */
+	u8 PKR[WPS_PK_LEN];
+
+	/* Registrar's Hashes */
+	u8 RHASH1[SHA256_MAC_LEN];
+	u8 RHASH2[SHA256_MAC_LEN];
+
+	u8 authKey[WPS_AUTHKEY_LEN];
+	u8 keyWrapKey[WPS_KEYWRAPKEY_LEN];
+	u8 emsk[WPS_EMSK_LEN];
+
+	u8 *prv_msg;
+	size_t prv_msg_len;
+
+	u8 *ssid;
+	size_t ssid_len;
+	u8 *network_key;
+	size_t network_key_len;
+	u16 auth_type;
+	u16 encryption_type;
+};
+
+
+static int wps_dh_key(struct eap_wps_data *data);
+static int eap_wps_kdk(struct eap_wps_data *data, u8 *enrolleeMac, u8 *kdk);
+static int eap_wps_kdf(struct eap_wps_data *data, const u8 *kdk);
+static int eap_wps_hash(u8 *authKey, u8 *PSK, u8 *PKE, u8 *PKR, u8 *ES,
+		char *dev_passwd, size_t dev_passwd_len, u8 *hash);
+static int eap_wps_authenticator(u8 *authKey, const u8 *d1, size_t d1_len,
+		u8 *d2, size_t d2_len, u8 *auth);
+static int eap_wps_authenticator_check(u8 *authKey,
+		const u8 *prv, size_t prv_len,
+		const u8 *cur, size_t cur_len,
+		const u8 *auth);
+static int eap_wps_encrypted_settings(u8 *authKey, u8 *keyWrapKey,
+		u16 type, u8 *data, size_t data_len,
+		u8 *encrypted);
+static int eap_wps_decrypt_settings(struct eap_wps_data *data, u8 *iv,
+		u8 *encrypted, size_t encrypted_len,
+		u8 *decrypted, size_t *decrypted_len);
+
+
+static u8 * eap_wps_write_tlv(u8 *buf, u16 type, u16 len, u8 *val)
+{
+	u8 *start;
+
+	WPA_PUT_BE16(buf, type);
+	WPA_PUT_BE16(buf + 2, len);
+
+	start = WPS_TLV_GET_VAL(buf);
+	os_memcpy(start, val, len);
+	start += len;
+
+	return start;
+}
+
+
+static inline u8 * eap_wps_write_tlv_u8(u8 *buf, u16 type, u8 val)
+{
+	return eap_wps_write_tlv(buf, type, 1, &val);
+}
+
+
+static inline u8 * eap_wps_write_tlv_u16(u8 *buf, u16 type, u16 val)
+{
+	u16 be_val = host_to_be16(val);
+
+	return eap_wps_write_tlv(buf, type, 2, (u8 *) &be_val);
+}
+
+
+/**
+ * eap_wps_msg_M1 - Create the payload for a M1 message
+ */
+static u8 *eap_wps_msg_M1(struct eap_wps_data *data, struct wpa_ssid *ssid,
+		size_t *len)
+{
+	u8 *payload = NULL, *p;
+	u16 val16;
+	u8 uuid_e[WPS_UUID_LEN];
+
+	*len = 0;
+
+	if (ssid->uuid_e) {
+		if (hexstr2bin(ssid->uuid_e, uuid_e, WPS_UUID_LEN)) {
+			wpa_printf(MSG_ERROR, "EAP-WPS: bad UUID format %s", ssid->uuid_e);
+			return NULL;
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-WPS: UUID", uuid_e, WPS_UUID_LEN);
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_MSG;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M1);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_UUID_E, WPS_UUID_LEN, uuid_e);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_MAC_ADDRESS, ETH_ALEN,
+				ssid->own_addr);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN,
+				data->N1);
+
+		if (wps_dh_key(data)) {
+			os_free(payload);
+			return NULL;
+		}
+		p = eap_wps_write_tlv(p, WPS_ELEM_PUBLIC_KEY, WPS_PK_LEN, data->PKE);
+
+		val16 = 0;
+		if (WPA_AUTH_ALG_SHARED == ssid->auth_alg) {
+			val16 |= WPS_AUTHENTICATION_TYPE_Shared;
+		} else {
+			val16 |= WPS_AUTHENTICATION_TYPE_OPEN;
+		}
+		/* Only support PSK although other values are defined */
+		val16 |=
+			WPS_AUTHENTICATION_TYPE_WPAPSK |
+			WPS_AUTHENTICATION_TYPE_WPA2PSK;
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_AUTHENTICATION_TYPE_FLAG,
+				val16);
+
+		val16 = 0;
+		if ((ssid->pairwise_cipher & WPA_CIPHER_TKIP) ||
+				(ssid->group_cipher & WPA_CIPHER_TKIP)) {
+			val16 |= WPS_ENCRYPTION_TYPE_TKIP;
+		}
+		if ((ssid->pairwise_cipher & WPA_CIPHER_CCMP) ||
+				(ssid->group_cipher & WPA_CIPHER_CCMP)) {
+			val16 |= WPS_ENCRYPTION_TYPE_AES;
+		}
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_ENCRYPTION_TYPE_FLAG,
+				val16);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_CONNECTION_TYPE_FLAG,
+				WPS_CONNECTION_TYPE_ESS);
+	
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_CONFIG_METHODS,
+				WPS_CONFIG_METHOD_LABEL |
+				WPS_CONFIG_METHOD_DISPLAY |
+				WPS_CONFIG_METHOD_PBC);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_WPS_STATE,
+				WPS_SETUP_STATE_NOT_CONFIGURED);
+
+		/* TODO get these somehow instead of using "None" and PC type */
+		p = eap_wps_write_tlv(p, WPS_ELEM_MANUFACTURER, 4, (u8 *)"None");
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_MODEL_NAME, 4, (u8 *)"None");
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_MODEL_NUMBER, 4, (u8 *)"None");
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_SERIAL_NUMBER, 4, (u8 *)"None");
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_PRIMARY_DEVICE_TYPE, 8,
+				(u8 *)"\x00\x01\x00\x50\xf2\x04\x00\x01");
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_DEVICE_NAME, 4, (u8 *)"None");
+
+		/* TODO get this somehow instead of assuming a dual-mode device */
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_RF_BANDS,
+				WPS_RF_BAND_2_4GHZ | WPS_RF_BAND_5_0GHZ);
+
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_ASSOCIATION_STATE,
+				WPS_ASSOCIATION_STATE_NOT_ASSOCIATED);
+
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_DEVICE_PASSWORD_ID,
+				0 == os_strncmp(ssid->wps_pin, WPS_PIN_PBC, sizeof(WPS_PIN_PBC)) ?
+				WPS_DEVICE_PASSWORD_ID_PBC : WPS_DEVICE_PASSWORD_ID_PIN);
+
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_CONFIGURATION_ERROR,
+				WPS_CONFIGURATION_ERROR_NO_ERROR);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_OS_VERSION, 4, (u8 *)"\x00\x00\x00\x01");
+	}
+
+	*len = (size_t)p - (size_t)payload;
+
+	return payload;
+}
+
+
+/**
+ * eap_wps_parse_M2 - Parse the payload for a M2 message
+ */
+static int eap_wps_parse_M2(struct eap_sm *sm, struct eap_wps_data *data,
+		const u8 *payload, size_t len, u8 **auth)
+{
+	const u8 *pos = payload;
+	const u8 *end = pos + len;
+	u16		tlv_type, tlv_len, tlv_val;
+	int ret = -1;
+
+	*auth = NULL;
+
+	if (WPS_OPCODE_WSC_MSG != *pos) {
+		wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)",
+				*pos);
+		wpa_hexdump(MSG_WARNING, "message:", pos, len);
+		return -1;
+	}
+
+	pos++;		/* Opcode */
+	pos++;		/* Flags */
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_MESSAGE_TYPE:
+			switch (pos[0]) {
+			case WPS_MESSAGE_TYPE_M2:
+				ret = 0;
+				break;
+			case WPS_MESSAGE_TYPE_M2D:
+				wpa_printf(MSG_DEBUG, "EAP-WPS: received M2D");
+				ret = WPS_M2D_ERR;
+				break;
+			default:
+				wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M2 but got %#x",
+						pos[0]);
+				return WPS_PARSE_ERR;
+			}
+			break;
+		case WPS_ELEM_REGISTRAR_NONCE:
+			os_memcpy(data->N2, pos, WPS_NONCE_LEN);
+			break;
+		case WPS_ELEM_PUBLIC_KEY:
+			os_memcpy(data->PKR, pos, WPS_PK_LEN);
+			break;
+		case WPS_ELEM_CONFIGURATION_ERROR:
+			tlv_val = WPA_GET_BE16(pos);
+			if (tlv_val > config_err_str_len) {
+				wpa_printf(MSG_ERROR, "EAP-WPS: Bad M2 Configuration error value %#x",
+						tlv_val);
+				return -1;
+			} else if (WPS_CONFIGURATION_ERROR_MULTI_PBC == tlv_val) {
+				wpa_msg(sm->msg_ctx, MSG_ERROR, WPA_EVENT_WPS_OVERLAP);
+				return -1;
+			} else if (WPS_CONFIGURATION_ERROR_NO_ERROR != tlv_val) {
+				wpa_msg(sm->msg_ctx, MSG_ERROR, WPA_EVENT_WPS_ERROR);
+				wpa_printf(MSG_WARNING, "EAP-WPS: M2 Configuration error %s",
+						config_err_str[tlv_val]);
+				return -1;
+			}
+			break;
+		case WPS_ELEM_AUTHENTICATOR:
+			*auth = (u8 *)pos;
+			break;
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	return ret;
+}
+
+/**
+ * eap_wps_msg_M3 - Create the payload for a M3 message
+ */
+static u8 *eap_wps_msg_M3(struct eap_wps_data *data, const u8 *M2,
+		size_t M2_len, u8 *addr, char *pin, size_t *len)
+{
+	size_t half_pin_len;
+	u8 *payload = NULL, *p;
+	u8 kdk[EAP_WPS_KDK_LEN];
+	u8 hash[SHA256_MAC_LEN];
+	u8 authenticator[WPS_AUTHENTICATOR_LEN];
+
+	*len = 0;
+
+	if (NULL == pin) {
+		wpa_printf(MSG_ERROR, "EAP-WPS: PIN is NULL?!?");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP-WPS: pin %s", pin);
+
+	half_pin_len = os_strlen(pin) / 2;
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_MSG;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M3);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN,
+				data->N2);
+
+		/* Generate a KDK used to create the AuthKey, KeyWrapKey, and EMSK */
+		if (eap_wps_kdk(data, addr, kdk)) {
+			return NULL;
+		}
+
+		eap_wps_kdf(data, kdk);
+
+		eap_wps_hash(data->authKey, data->PSK1, data->PKE, data->PKR, data->ES1,
+				pin, half_pin_len,
+				hash);
+		p = eap_wps_write_tlv(p, WPS_ELEM_E_HASH1, sizeof(hash), hash);
+
+		eap_wps_hash(data->authKey, data->PSK2, data->PKE, data->PKR, data->ES2,
+				pin + half_pin_len, half_pin_len,
+				hash);
+		p = eap_wps_write_tlv(p, WPS_ELEM_E_HASH2, sizeof(hash), hash);
+
+		*len = (size_t)p - (size_t)payload;
+
+		/*
+		 * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M2 | M3*)
+		 * using the AuthKey.
+		 * Note that M2 and M3 do not include the Opcode or Flags (thus +/- 2)
+		 */
+		eap_wps_authenticator(data->authKey, M2 + 2, M2_len - 2, payload + 2,
+				*len - 2, authenticator);
+		p = eap_wps_write_tlv(p, WPS_ELEM_AUTHENTICATOR, WPS_AUTHENTICATOR_LEN,
+				authenticator);
+
+		*len = (size_t)p - (size_t)payload;
+	}
+
+	return payload;
+}
+
+
+/**
+ * eap_wps_parse_encrypted_settings_snonce - Parse the encrypted settings
+ *
+ * TODO fail if any required element is missing (?)
+ */
+static int eap_wps_parse_encrypted_settings_snonce(struct eap_wps_data *data,
+		const u8 *settings, size_t len)
+{
+	const u8 *pos = settings;
+	const u8 *end;
+	u16		tlv_type, tlv_len;
+	const u8 *addr[4];
+	size_t vlen[4];
+	u8 hash[SHA256_MAC_LEN];
+	u8 *expect = NULL;
+	char *nonce_str = NULL;
+
+	end = settings + len;
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_R_SNONCE1:
+			addr[0] = pos;
+			addr[1] = data->PSK1;
+			expect = data->RHASH1;
+			nonce_str = "R-SNonce1";
+			break;
+		case WPS_ELEM_R_SNONCE2:
+			addr[0] = pos;
+			addr[1] = data->PSK2;
+			expect = data->RHASH2;
+			nonce_str = "R-SNonce2";
+			break;
+		case WPS_ELEM_KEY_WRAP_AUTHENTICATOR:
+			if (eap_wps_authenticator_check(data->authKey,
+				settings, len - WPS_TLV_LEN - WPS_AUTHENTICATOR_LEN,
+				NULL, 0, pos))
+			{
+				wpa_printf(MSG_ERROR,
+						"EAP-WPS: Key wrap authenticator check fail "
+						"Encrypted Settings %s", nonce_str);
+				return -1;
+			}
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "EAP-WPS: unknown type (%#x)", tlv_type);
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	vlen[0]  = WPS_S_NONCE_LEN;
+	vlen[1]  = SHA256_MAC_LEN/2;	/* only use the first 128-bits */
+	addr[2] = data->PKE;
+	vlen[2]  = WPS_PK_LEN;
+	addr[3] = data->PKR;
+	vlen[3]  = WPS_PK_LEN;
+
+	hmac_sha256_vector(data->authKey, WPS_AUTHKEY_LEN, 4, addr, vlen, hash);
+
+	return os_memcmp(expect, hash, SHA256_MAC_LEN);
+}
+
+
+/**
+ * eap_wps_parse_M4 - Parse the payload for a M4 message
+ * @data: Pointer to WPS data
+ * @payload: Message buffer to parse
+ * @len: Length of message buffer
+ * Returns: 0 if payload is a valid M4 message, -1 if not a valid M4
+ * 			message, or -2 if the Authenticator check fails
+ */
+static int eap_wps_parse_M4(struct eap_wps_data *data, const u8 *payload,
+		size_t len)
+{
+	const u8 *pos = payload;
+	const u8 *end = pos + len;
+	u16		tlv_type, tlv_len;
+	u8		*settings = NULL;
+	size_t	settings_len;
+	int ret = -1;
+
+	if (WPS_OPCODE_WSC_MSG != *pos) {
+		wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)",
+				*pos);
+		wpa_hexdump(MSG_WARNING, "message:", pos, len);
+		return WPS_PARSE_ERR;
+	}
+
+	pos++;		/* Opcode */
+	pos++;		/* Flags */
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_MESSAGE_TYPE:
+			if (WPS_MESSAGE_TYPE_M4 != pos[0]) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M4 but got %#x",
+						pos[0]);
+				return WPS_PARSE_ERR;
+			} else {
+				ret = 0;
+			}
+			break;
+		case WPS_ELEM_R_HASH1:
+			os_memcpy(data->RHASH1, pos, SHA256_MAC_LEN);
+			break;
+		case WPS_ELEM_R_HASH2:
+			os_memcpy(data->RHASH2, pos, SHA256_MAC_LEN);
+			break;
+		case WPS_ELEM_ENCRYPTED_SETTINGS:
+			if (NULL != (settings = os_malloc(tlv_len))) {
+				/* first 128 bits are IV. remaining bits are the encrypted data */
+				eap_wps_decrypt_settings(data, (u8 *)pos,
+						(u8 *)pos + WPS_IV_LEN, tlv_len - WPS_IV_LEN,
+						settings, &settings_len);
+	
+				if (eap_wps_parse_encrypted_settings_snonce(data, settings,
+							settings_len)) {
+					os_free(settings);
+					settings = NULL;
+					settings_len = 0;
+					wpa_printf(MSG_ERROR, "EAP-WPS: Incorrect encrypted settings M4");
+					return WPS_PARSE_ERR;
+				}
+			}
+			break;
+		case WPS_ELEM_AUTHENTICATOR:
+			if (eap_wps_authenticator_check(data->authKey,
+				data->prv_msg + 2, data->prv_msg_len - 2,
+				payload + 2, len - 2, pos))
+			{
+				wpa_printf(MSG_ERROR, "EAP-WPS: M4 authenticator check fail");
+				return WPS_AUTHENTICATOR_ERR;
+			}
+			break;
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	return ret;
+}
+
+/**
+ * eap_wps_msg_M5 - Create the payload for a M5 message
+ */
+static u8 *eap_wps_msg_M5(struct eap_wps_data *data, const u8 *M4, size_t M4_len, size_t *len)
+{
+	u8 *payload = NULL, *p;
+	u8 encSetting[WPS_ENCRYPTED_SETTINGS_LEN];
+	u8 authenticator[WPS_AUTHENTICATOR_LEN];
+
+	*len = 0;
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_MSG;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M5);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN,
+				data->N2);
+
+		eap_wps_encrypted_settings(data->authKey, data->keyWrapKey, WPS_ELEM_E_SNONCE1,
+				data->ES1, WPS_S_NONCE_LEN, encSetting);
+		p = eap_wps_write_tlv(p, WPS_ELEM_ENCRYPTED_SETTINGS, sizeof(encSetting),
+				encSetting);
+
+		*len = (size_t)p - (size_t)payload;
+
+		/*
+		 * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M4 | M5*)
+		 * using the AuthKey.
+		 * Note that M4 and M5 do not include the Opcode or Flags (thus +/- 2)
+		 */
+		eap_wps_authenticator(data->authKey, M4 + 2, M4_len - 2, payload + 2, *len - 2,
+				authenticator);
+		p = eap_wps_write_tlv(p, WPS_ELEM_AUTHENTICATOR, WPS_AUTHENTICATOR_LEN,
+				authenticator);
+
+		*len = (size_t)p - (size_t)payload;
+	}
+
+	return payload;
+}
+
+/**
+ * eap_wps_parse_M6 - Parse the payload for a M6 message
+ * @data: Pointer to WPS data
+ * @payload: Message buffer to parse
+ * @len: Length of message buffer
+ * Returns: 0 if payload is a valid M6 message, -1 if not a valid M6
+ * 			message, or -2 if the Authenticator check fails
+ */
+static int eap_wps_parse_M6(struct eap_wps_data *data, const u8 *payload,
+		size_t len)
+{
+	const u8 *pos = payload;
+	const u8 *end = pos + len;
+	u16		tlv_type, tlv_len;
+	u8		*settings = NULL;
+	size_t	settings_len;
+	int ret = -1;
+
+	if (WPS_OPCODE_WSC_MSG != *pos) {
+		wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)",
+				*pos);
+		wpa_hexdump(MSG_WARNING, "message:", pos, len);
+		return WPS_PARSE_ERR;
+	}
+
+	pos++;		/* Opcode */
+	pos++;		/* Flags */
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_MESSAGE_TYPE:
+			if (WPS_MESSAGE_TYPE_M6 != pos[0]) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M6 but got %#x",
+						pos[0]);
+				return WPS_PARSE_ERR;
+			} else {
+				ret = 0;
+			}
+			break;
+		case WPS_ELEM_ENCRYPTED_SETTINGS:
+			if (NULL != (settings = os_malloc(tlv_len))) {
+				/* first 128 bits are IV. remaining bits are the encrypted data */
+				eap_wps_decrypt_settings(data, (u8 *)pos,
+						(u8 *)pos + WPS_IV_LEN, tlv_len - WPS_IV_LEN,
+						settings, &settings_len);
+	
+				if (eap_wps_parse_encrypted_settings_snonce(data, settings,
+							settings_len)) {
+					os_free(settings);
+					settings = NULL;
+					settings_len = 0;
+					wpa_printf(MSG_ERROR, "EAP-WPS: Incorrect encrypted settings M6");
+					return WPS_PARSE_ERR;
+				}
+			}
+			break;
+		case WPS_ELEM_AUTHENTICATOR:
+			if (eap_wps_authenticator_check(data->authKey,
+				data->prv_msg + 2, data->prv_msg_len - 2,
+				payload + 2, len - 2, pos))
+			{
+				wpa_printf(MSG_ERROR, "EAP-WPS: M6 authenticator check fail");
+				return WPS_AUTHENTICATOR_ERR;
+			}
+		break;
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	return ret;
+}
+
+/**
+ * eap_wps_msg_M7 - Create the payload for a M7 message
+ */
+static u8 *eap_wps_msg_M7(struct eap_wps_data *data, const u8 *M6, size_t M6_len, size_t *len)
+{
+	u8 *payload = NULL, *p;
+	u8 encSetting[WPS_ENCRYPTED_SETTINGS_LEN];
+	u8 authenticator[WPS_AUTHENTICATOR_LEN];
+
+	*len = 0;
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_MSG;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_M7);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN,
+				data->N2);
+
+		eap_wps_encrypted_settings(data->authKey, data->keyWrapKey, WPS_ELEM_E_SNONCE2,
+				data->ES2, WPS_S_NONCE_LEN, encSetting);
+		p = eap_wps_write_tlv(p, WPS_ELEM_ENCRYPTED_SETTINGS, sizeof(encSetting),
+				encSetting);
+
+		*len = (size_t)p - (size_t)payload;
+
+		/*
+		 * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M6 | M7*)
+		 * using the AuthKey.
+		 * Note that M6 and M7 do not include the Opcode or Flags (thus +/- 2)
+		 */
+		eap_wps_authenticator(data->authKey, M6 + 2, M6_len - 2, payload + 2, *len - 2,
+				authenticator);
+		p = eap_wps_write_tlv(p, WPS_ELEM_AUTHENTICATOR, WPS_AUTHENTICATOR_LEN,
+				authenticator);
+
+		*len = (size_t)p - (size_t)payload;
+	}
+
+	return payload;
+}
+
+
+/**
+ * eap_wps_parse_credential - Parse the Credentials element
+ */
+static int eap_wps_parse_credential(struct eap_wps_data *data,
+		const u8 *credential, size_t len)
+{
+	const u8 *pos = credential;
+	const u8 *end;
+	u16		tlv_type, tlv_len;
+	end = credential + len;
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_NETWORK_INDEX:
+			break;
+		case WPS_ELEM_SSID:
+			if (NULL != (data->ssid = os_malloc(tlv_len))) {
+				os_memcpy(data->ssid, pos, tlv_len);
+				data->ssid_len = tlv_len;
+
+				wpa_printf(MSG_WARNING, "EAP-WPS: Credential SSID %s",
+							wpa_ssid_txt((u8 *)pos, tlv_len));
+			}
+			break;
+		case WPS_ELEM_AUTHENTICATION_TYPE:
+			data->auth_type = WPA_GET_BE16(pos);
+			wpa_printf(MSG_DEBUG, "EAP-WPS: auth_type=0x%04x", data->auth_type);
+			break;
+		case WPS_ELEM_ENCRYPTION_TYPE:
+			data->encryption_type = WPA_GET_BE16(pos);
+			wpa_printf(MSG_DEBUG, "EAP-WPS: encryption_type=0x%04x", data->encryption_type);
+			break;
+		case WPS_ELEM_NETWORK_KEY:
+			if (data->network_key) {
+				os_free(data->network_key);
+			}
+			if (NULL == (data->network_key = os_malloc(tlv_len))) {
+				wpa_printf(MSG_ERROR, "EAP-WPS: Cannot allocate network key");
+				return -1;
+			}
+			os_memcpy(data->network_key, pos, tlv_len);
+			data->network_key_len = tlv_len;
+			break;
+		case WPS_ELEM_MAC_ADDRESS:
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "EAP-WPS: unknown type %#x (credential)",
+					tlv_type);
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	return 0;
+}
+
+/**
+ * eap_wps_parse_encrypted_settings_sta - Parse the encrypted settings
+ *
+ * TODO fail if any required element is missing (?)
+ */
+static int eap_wps_parse_encrypted_settings_sta(struct eap_wps_data *data,
+		const u8 *settings, size_t len)
+{
+	const u8 *pos = settings;
+	const u8 *end;
+	u16		tlv_type, tlv_len;
+
+	end = settings + len;
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_CREDENTIAL:
+			eap_wps_parse_credential(data, pos, tlv_len);
+			break;
+		case WPS_ELEM_KEY_WRAP_AUTHENTICATOR:
+			if (eap_wps_authenticator_check(data->authKey,
+				settings, len - WPS_TLV_LEN - WPS_AUTHENTICATOR_LEN,
+				NULL, 0, pos))
+			{
+				wpa_printf(MSG_ERROR,
+						"EAP-WPS: Key wrap authenticator check fail "
+						"Encrypted Settings STA");
+				return -1;
+			}
+	
+			/* Setting pos exits while loop to avoid PKCS#5 v2.0 padding */
+			pos = end;
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "EAP-WPS: unknown type (%#x) STA", tlv_type);
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	return 0;
+}
+
+/**
+ * eap_wps_parse_M8 - Parse the payload for a M8 message
+ * @data: Pointer to WPS data
+ * @payload: Message buffer to parse
+ * @len: Length of message buffer
+ * Returns: 0 if payload is a valid M8 message, -1 if not a valid M8
+ * 			message, or -2 if the Authenticator check fails
+ */
+static int eap_wps_parse_M8(struct eap_wps_data *data, const u8 *payload,
+		size_t len)
+{
+	const u8 *pos = payload;
+	const u8 *end = pos + len;
+	u16		tlv_type, tlv_len;
+	u8		*settings = NULL;
+	size_t	settings_len;
+	int		ret = -1;
+
+	if (WPS_OPCODE_WSC_MSG != *pos) {
+		wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_MSG (%#02x)",
+				*pos);
+		wpa_hexdump(MSG_WARNING, "message:", pos, len);
+		return WPS_PARSE_ERR;
+	}
+
+	pos++;		/* Opcode */
+	pos++;		/* Flags */
+
+	do {
+		tlv_type = WPS_TLV_GET_TYPE(pos);
+		tlv_len  = WPS_TLV_GET_LEN(pos);
+		pos = WPS_TLV_GET_VAL(pos);
+
+		switch (tlv_type) {
+		case WPS_ELEM_MESSAGE_TYPE:
+			if (WPS_MESSAGE_TYPE_M8 != pos[0]) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Expecting message M8 but got %#x",
+						pos[0]);
+				return WPS_PARSE_ERR;
+			} else {
+				ret = 0;
+			}
+			break;
+		case WPS_ELEM_ENCRYPTED_SETTINGS:
+			if (NULL != (settings = os_malloc(tlv_len))) {
+				/* first 128 bits are IV. remaining bits are the encrypted data */
+				eap_wps_decrypt_settings(data, (u8 *)pos,
+						(u8 *)pos + WPS_IV_LEN, tlv_len - WPS_IV_LEN,
+						settings, &settings_len);
+
+				if (eap_wps_parse_encrypted_settings_sta(data, settings,
+							settings_len)) {
+					os_free(settings);
+					settings = NULL;
+					settings_len = 0;
+					wpa_printf(MSG_ERROR, "EAP-WPS: Incorrect encrypted settings M8");
+					return WPS_PARSE_ERR;
+				}
+			}
+			break;
+		case WPS_ELEM_AUTHENTICATOR:
+			if (eap_wps_authenticator_check(data->authKey,
+				data->prv_msg + 2, data->prv_msg_len - 2,
+				payload + 2, len - 2, pos))
+			{
+				wpa_printf(MSG_ERROR, "EAP-WPS: M8 authenticator check fail");
+				return WPS_AUTHENTICATOR_ERR;
+			}
+			break;
+		}
+
+		pos += tlv_len;
+	} while (pos < end);
+
+	return ret;
+}
+
+/**
+ * eap_wps_msg_Done - Create the payload for a WSC_Done message
+ */
+static u8 *eap_wps_msg_Done(struct eap_wps_data *data, size_t *len)
+{
+	u8 *payload = NULL, *p;
+
+	*len = 0;
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_Done;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE, WPS_MESSAGE_TYPE_WSC_Done);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN,
+				data->N1);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN,
+				data->N2);
+
+		*len = (size_t)p - (size_t)payload;
+	}
+
+	return payload;
+}
+
+
+static u8 *eap_wps_ack(struct eap_wps_data *data, size_t *len)
+{
+	u8 *payload = NULL, *p;
+
+	*len = 0;
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_ACK;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE,
+				WPS_MESSAGE_TYPE_WSC_ACK);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN,
+				data->N1);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN,
+				data->N2);
+
+		*len = (size_t)p - (size_t)payload;
+	}
+
+	return payload;
+}
+
+static u8 *eap_wps_nack(struct eap_wps_data *data, size_t *len, u16 error)
+{
+	u8 *payload = NULL, *p;
+
+	*len = 0;
+
+	if (NULL != (p = payload = os_malloc(WPS_MSG_BUF_LEN))) {
+		*p++ = WPS_OPCODE_WSC_NACK;
+		*p++ = 0;	/* Flags */
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_VERSION, WPS_VERSION_1_0);
+
+		p = eap_wps_write_tlv_u8(p, WPS_ELEM_MESSAGE_TYPE,
+				WPS_MESSAGE_TYPE_WSC_NACK);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_ENROLLEE_NONCE, WPS_NONCE_LEN,
+				data->N1);
+
+		p = eap_wps_write_tlv(p, WPS_ELEM_REGISTRAR_NONCE, WPS_NONCE_LEN,
+				data->N2);
+
+		p = eap_wps_write_tlv_u16(p, WPS_ELEM_CONFIGURATION_ERROR, error);
+
+		*len = (size_t)p - (size_t)payload;
+	}
+
+	return payload;
+}
+
+
+static void * eap_wps_init(struct eap_sm *sm)
+{
+	struct eap_wps_data *data;
+
+	data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+
+	data->state = WPS_START;
+
+	return data;
+}
+
+
+static void eap_wps_deinit(struct eap_sm *sm, void *priv)
+{
+	struct eap_wps_data *data = priv;
+
+	if (data == NULL)
+		return;
+	os_free(data->prv_msg);
+	os_free(data->ssid);
+	os_free(data->network_key);
+	os_free(data);
+}
+
+
+static u8 * eap_wps_process(struct eap_sm *sm, void *priv,
+			    struct eap_method_ret *ret,
+			    const u8 *reqData, size_t reqDataLen,
+			    size_t *respDataLen)
+{
+	struct eap_wps_data *data = priv;
+	struct wpa_ssid *ssid = NULL;
+	const u8 *pos;
+	size_t len;
+	u8 *rpos, *msg = NULL;
+	size_t msg_len;
+	const struct eap_hdr *req;
+	struct eap_hdr *resp = NULL;
+	u8 *auth = NULL;
+	int err = 0;
+
+	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_TYPE_SIMPLE_CONFIG,
+			reqData, reqDataLen, &len);
+
+	if (!sm->rxFailure && ((NULL == pos) || (0 == len))) {
+		wpa_printf(MSG_WARNING, "EAP-WPS: Invalid frame (pos=%p len=%lu)",
+				pos, (unsigned long) len);
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	if (NULL == (ssid = eap_get_config(sm))) {
+		wpa_printf(MSG_WARNING, "EAP-WPS: Cannot get configuration");
+		ret->ignore = TRUE;
+		return NULL;
+	}
+
+	req = (const struct eap_hdr *) reqData;
+	
+	if (WPS_OPCODE_WSC_NACK == *pos) {
+		ret->ignore = FALSE;
+		ret->decision = DECISION_FAIL;
+		ret->methodState = METHOD_DONE;
+
+		if (NULL == (msg = eap_wps_nack(data, &msg_len,
+						WPS_CONFIGURATION_ERROR_NO_ERROR))) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK");
+			ret->ignore = TRUE;
+		}
+
+		data->state = WPS_START;
+	} else switch (data->state) {
+	case WPS_START:
+		/* Clear all state including keys and nonces */
+		ssid->assoc_state = WPS_ASSOCIATION_STATE_CONNECTION_FAILURE;
+		os_free(data->prv_msg);
+		os_free(data->ssid);
+		os_free(data->network_key);
+		os_memset(data, 0, sizeof(struct eap_wps_data));
+
+		if (WPS_OPCODE_WSC_Start != *pos) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Message is not WSC_Start (%#02x)",
+					*pos);
+			ret->ignore = TRUE;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-WPS: Received WSC_Start");
+
+		ret->ignore = FALSE;
+		ret->methodState = METHOD_MAY_CONT;
+
+		if (os_get_random(data->N1, WPS_NONCE_LEN)) {
+			wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for N1");
+			break;
+		}
+		wpa_hexdump(MSG_DEBUG, "EAP-WPS: N1 (Enrollee nonce)",
+				data->N1, WPS_NONCE_LEN);
+
+		if (NULL == (msg = eap_wps_msg_M1(data, ssid, &msg_len))) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M1");
+			ret->ignore = TRUE;
+			break;
+		}
+
+		data->state = WPS_M2;
+		break;
+	case WPS_M2:
+		/* Validate that this is a M2 message */
+		err = eap_wps_parse_M2(sm, data, pos, len, &auth);
+		if (WPS_PARSE_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M2");
+			ret->ignore = TRUE;
+			break;
+		} else if (WPS_M2D_ERR == err) {
+			if (NULL == (msg = eap_wps_ack(data, &msg_len))) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_ACK");
+			}
+			ret->ignore = FALSE;
+			ret->decision = DECISION_FAIL;
+			ret->methodState = METHOD_DONE;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-WPS: Received M2");
+
+		ret->ignore = FALSE;
+		ret->methodState = METHOD_MAY_CONT;
+
+		/* build message M3 */
+		if (NULL == (msg = eap_wps_msg_M3(data, pos, len, ssid->own_addr,
+						ssid->wps_pin, &msg_len))) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M3");
+			ret->ignore = TRUE;
+			break;
+		}
+
+		/*
+		 * Normally you would want to verify the authenticator in the
+		 * parse routine above, but the AuthKey can't be calculated
+		 * until after parsing the incoming message for N2
+		 */
+		if (eap_wps_authenticator_check(data->authKey,
+			data->prv_msg + 2, data->prv_msg_len - 2,
+			pos + 2, len - 2, auth))
+		{
+			wpa_printf(MSG_ERROR, "EAP-WPS: M2 authenticator check fail");
+			os_free(msg);
+			msg = NULL;
+			msg_len = 0;
+			ret->ignore = TRUE;
+			break;
+		}
+
+		data->state = WPS_M4;
+		break;
+	case WPS_M4:
+		err = eap_wps_parse_M4(data, pos, len);
+		if (WPS_PARSE_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M4");
+			ret->ignore = FALSE;
+			ret->decision = DECISION_FAIL;
+			ret->methodState = METHOD_DONE;
+
+			if (NULL == (msg = eap_wps_nack(data, &msg_len,
+							WPS_CONFIGURATION_ERROR_NO_ERROR))) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK");
+				ret->ignore = TRUE;
+			}
+
+			data->state = WPS_START;
+			break;
+		} else if (WPS_AUTHENTICATOR_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: M4 authenticator invalid");
+			ret->ignore = TRUE;
+			ret->methodState = METHOD_MAY_CONT;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-WPS: Received M4");
+
+		ret->ignore = FALSE;
+		ret->methodState = METHOD_MAY_CONT;
+
+		/* build message M5 */
+		if (NULL == (msg = eap_wps_msg_M5(data, pos, len, &msg_len))) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M5");
+			ret->ignore = TRUE;
+			break;
+		}
+
+		data->state = WPS_M6;
+		break;
+	case WPS_M6:
+		err = eap_wps_parse_M6(data, pos, len);
+		if (WPS_PARSE_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M6");
+			ret->ignore = FALSE;
+			ret->decision = DECISION_FAIL;
+			ret->methodState = METHOD_DONE;
+
+			if (NULL == (msg = eap_wps_nack(data, &msg_len,
+							WPS_CONFIGURATION_ERROR_NO_ERROR))) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK");
+				ret->ignore = TRUE;
+			}
+
+			data->state = WPS_START;
+			break;
+		} else if (WPS_AUTHENTICATOR_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: M6 authenticator invalid");
+			ret->ignore = TRUE;
+			ret->methodState = METHOD_MAY_CONT;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-WPS: Received M6");
+
+		ret->ignore = FALSE;
+		ret->methodState = METHOD_MAY_CONT;
+
+		/* build message M7 */
+		if (NULL == (msg = eap_wps_msg_M7(data, pos, len, &msg_len))) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message M7");
+			ret->ignore = TRUE;
+			break;
+		}
+
+		data->state = WPS_M8;
+		break;
+	case WPS_M8:
+		err = eap_wps_parse_M8(data, pos, len);
+		if (WPS_PARSE_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Message is not M8");
+			ret->ignore = FALSE;
+			ret->decision = DECISION_FAIL;
+			ret->methodState = METHOD_DONE;
+
+			/* TODO can we do better than NO_ERROR */
+			if (NULL == (msg = eap_wps_nack(data, &msg_len,
+							WPS_CONFIGURATION_ERROR_NO_ERROR))) {
+				wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create WSC_NACK");
+				ret->ignore = TRUE;
+			}
+
+			data->state = WPS_START;
+			break;
+		} else if (WPS_AUTHENTICATOR_ERR == err) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: M8 authenticator invalid");
+			ret->ignore = TRUE;
+			ret->methodState = METHOD_MAY_CONT;
+			break;
+		}
+
+		wpa_printf(MSG_DEBUG, "EAP-WPS: Received M8");
+
+		ret->ignore = FALSE;
+		ret->methodState = METHOD_MAY_CONT;
+
+		/* build message WSC_Done */
+		if (NULL == (msg = eap_wps_msg_Done(data, &msg_len))) {
+			wpa_printf(MSG_WARNING, "EAP-WPS: Cannot create message WSC_Done");
+			ret->ignore = TRUE;
+			break;
+		}
+
+		if (data->auth_type & WPS_AUTHENTICATION_TYPE_Shared) {
+			ssid->auth_alg = WPA_AUTH_ALG_SHARED;
+			wpa_printf(MSG_DEBUG, "EAP-WPS: shared key authentication");
+		} else {
+			ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+			wpa_printf(MSG_DEBUG, "EAP-WPS: open system authentication");
+		}
+
+		if (NULL != data->ssid) {
+			os_free(ssid->ssid);
+
+			if (NULL != (ssid->ssid = os_malloc(data->ssid_len))) {
+				os_memcpy(ssid->ssid, data->ssid, data->ssid_len);
+				ssid->ssid_len = data->ssid_len;
+			}
+		}
+		
+		ssid->key_mgmt = 0;
+		ssid->proto = 0;
+		ssid->pairwise_cipher = 0;
+		ssid->group_cipher = 0;
+
+		if (data->auth_type & WPS_AUTHENTICATION_TYPE_OPEN) {
+			ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+		} else {
+			if (data->auth_type &
+					(WPS_AUTHENTICATION_TYPE_WPAPSK |
+					 WPS_AUTHENTICATION_TYPE_WPA)) {
+				ssid->proto |= WPA_PROTO_WPA;
+				wpa_printf(MSG_DEBUG, "EAP-WPS: protocol WPA");
+				if (data->auth_type & WPS_AUTHENTICATION_TYPE_WPAPSK) {
+					ssid->key_mgmt |= WPA_KEY_MGMT_PSK;
+					wpa_printf(MSG_DEBUG, "EAP-WPS: key management PSK");
+				}
+			}
+
+			if (data->auth_type &
+					(WPS_AUTHENTICATION_TYPE_WPA2PSK |
+					 WPS_AUTHENTICATION_TYPE_WPA2)) {
+				ssid->proto |= WPA_PROTO_RSN;
+				wpa_printf(MSG_DEBUG, "EAP-WPS: protocol RSN");
+				if (data->auth_type & WPS_AUTHENTICATION_TYPE_WPA2PSK) {
+					ssid->key_mgmt |= WPA_KEY_MGMT_PSK;
+					wpa_printf(MSG_DEBUG, "EAP-WPS: key management PSK");
+				}
+			}
+		}
+
+		if (data->encryption_type & WPS_ENCRYPTION_TYPE_NONE) {
+			ssid->pairwise_cipher |= WPA_CIPHER_NONE;
+			ssid->group_cipher |= WPA_CIPHER_NONE;
+			wpa_printf(MSG_DEBUG, "EAP-WPS: cipher NONE");
+		}
+
+		if (data->encryption_type & WPS_ENCRYPTION_TYPE_WEP) {
+			ssid->pairwise_cipher |= WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104;
+			ssid->group_cipher |= WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104;
+			wpa_printf(MSG_DEBUG, "EAP-WPS: cipher WEP");
+		}
+
+		/*
+		 * WPS has a protocol problem in that a Credential contains a
+		 * single encryption type, but this doesn't account for AP that
+		 * use different ciphers for group and pairwise keys. The safest
+		 * approach is to enable both TKIP and CCMP, and let the upper
+		 * layer sort things out based on the advertised WPA/RSN IE.
+		 */
+		if (data->encryption_type & WPS_ENCRYPTION_TYPE_TKIP ||
+				data->encryption_type & WPS_ENCRYPTION_TYPE_AES) {
+			ssid->pairwise_cipher |= WPA_CIPHER_TKIP | WPA_CIPHER_CCMP;
+			ssid->group_cipher |= WPA_CIPHER_TKIP | WPA_CIPHER_CCMP;
+			wpa_printf(MSG_DEBUG, "EAP-WPS: cipher TKIP/CCMP");
+		}
+
+		if (data->network_key) {
+			wpa_hexdump_key(MSG_DEBUG, "EAP-WPS: Network key",
+					data->network_key, data->network_key_len);
+			if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+				if (data->network_key_len < WPS_WPAPSK_PASSPHRASE_LEN) {
+					size_t len = data->network_key_len;
+
+					ssid->psk_set = 0;
+					os_free(ssid->passphrase);
+					ssid->passphrase = os_malloc(len +1);
+					if (NULL == ssid->passphrase) {
+						wpa_printf(MSG_DEBUG, "EAP-WPS: alloc failed for passphrase");
+					} else {
+						os_memcpy(ssid->passphrase, data->network_key, len);
+						ssid->passphrase[len] = '\0';
+					}
+					wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-WPS: passphrase",
+							(u8 *) ssid->passphrase,
+							os_strlen(ssid->passphrase));
+					wpa_config_update_psk(ssid);
+				} else {
+					ssid->psk_set = 1;
+					hexstr2bin((const char *)data->network_key, ssid->psk,
+							PMK_LEN);
+				}
+				wpa_hexdump_key(MSG_DEBUG, "EAP-WPS: psk", ssid->psk,
+						PMK_LEN);
+			} else {
+				wpa_printf(MSG_ERROR, "EAP-WPS: key management not PSK");
+			}
+		}
+
+		ssid->assoc_state = WPS_ASSOCIATION_STATE_CONNECTION_SUCCESS;
+
+		data->state = WPS_START;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "EAP-WPS: Unhandled state %d", data->state);
+		ret->ignore = TRUE;
+	}
+
+	if (msg) {
+		void *tmp;
+
+		resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_TYPE_SIMPLE_CONFIG,
+				respDataLen, msg_len, EAP_CODE_RESPONSE,
+				req->identifier, &rpos);
+
+		if (resp) {
+			os_memcpy(rpos, msg, msg_len);
+		}
+		/*
+		 * Save a copy of our current message to use in the validation
+		 * of the next received message.
+		 */
+		tmp = data->prv_msg;
+		if (NULL != (data->prv_msg = os_realloc(data->prv_msg, msg_len))) {
+			os_memcpy(data->prv_msg, msg, msg_len);
+			data->prv_msg_len = msg_len;
+		} else {
+			data->prv_msg_len = 0;
+			os_free(tmp);
+		}
+
+		os_free(msg);
+	}
+
+	return (u8 *) resp;
+}
+
+
+int eap_peer_wps_register(void)
+{
+	struct eap_method *eap;
+	int ret;
+
+	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+				    EAP_VENDOR_WFA, EAP_TYPE_SIMPLE_CONFIG, "WPS");
+	if (eap == NULL)
+		return -1;
+
+	eap->init = eap_wps_init;
+	eap->deinit = eap_wps_deinit;
+	eap->process = eap_wps_process;
+
+	ret = eap_peer_method_register(eap);
+	if (ret)
+		eap_peer_method_free(eap);
+
+	return ret;
+}
+
+
+/*
+ * private key is random # a
+ * public  key is g^a mod p
+ *    where g = base (generator)
+ *    where p = prime number (modp)
+ */
+static int wps_dh_key(struct eap_wps_data *data)
+{
+	int		ret = 0;
+	size_t pke_len = WPS_PK_LEN;
+
+	if (os_get_random(data->SKE, sizeof(data->SKE))) {
+		wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for SKE");
+		return -1;
+	}
+
+	/*
+	 * Calculate the public key A = g^a mod p
+	 */
+	ret = crypto_mod_exp(&wps_dh_generator, 1,	/* g */
+			data->SKE, sizeof(data->SKE),		/* a */
+			wps_dh_modp, wps_dh_modp_size,		/* p */
+			data->PKE, &pke_len);
+
+	return ret;
+}
+
+static int eap_wps_kdk(struct eap_wps_data *data, u8 *enrolleeMac, u8 *kdk)
+{
+	u8 dh_key[WPS_PK_LEN];
+	size_t dh_key_len = WPS_PK_LEN;
+	const u8 *addr[3];
+	size_t len[3];
+	int ret = 0;
+
+	/* KDK = HMAC-SHA-256 dh_key (N1 || EnrolleeMAC || N2) */
+
+	ret = crypto_mod_exp(data->PKR, WPS_PK_LEN,	/* (g^b mod p) */
+			data->SKE, sizeof(data->SKE),		/* a */
+			wps_dh_modp, wps_dh_modp_size,		/* p */
+			dh_key, &dh_key_len);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-WPS: dh_key", dh_key, dh_key_len);
+
+	addr[0] = data->N1;
+	len[0]  = WPS_NONCE_LEN;
+	addr[1] = enrolleeMac;
+	len[1]  = ETH_ALEN;
+	addr[2] = data->N2;
+	len[2]  = WPS_NONCE_LEN;
+
+	hmac_sha256_vector(dh_key, dh_key_len, 3, addr, len, kdk);
+
+	return ret;
+}
+
+#define WPS_TOTAL_KEY_BITS	640
+static int eap_wps_kdf(struct eap_wps_data *data, const u8 *kdk)
+{
+	const char personalization[] = "Wi-Fi Easy and Secure Key Derivation";
+	/* #define for the PRF digest size (ie HMAC-SHA-256) is in bytes */
+	const u32 iterations =
+		(WPS_TOTAL_KEY_BITS + (SHA256_MAC_LEN * 8) - 1)/(SHA256_MAC_LEN * 8);
+	size_t	i;
+	u8		*keys = NULL, *k;
+	const u8 *addr[3];
+	size_t	len[3];
+	u32		iterations_be;
+	u32		total_key_bits_be;
+
+	if (NULL == (keys = os_zalloc(iterations * SHA256_MAC_LEN))) {
+		wpa_printf(MSG_ERROR, "EAP-WPS: Couldn't allocate KDF buffer");
+		return -1;
+	}
+
+	addr[0] = (u8 *)&iterations_be;
+	len[0]  = sizeof(u32);
+	addr[1] = (u8 *)personalization;
+	len[1]  = os_strlen(personalization);
+	addr[2] = (u8 *)&total_key_bits_be;
+	len[2]  = sizeof(u32);
+
+	k = keys;
+	total_key_bits_be = host_to_be32(WPS_TOTAL_KEY_BITS);
+	for (i=1; i<=iterations; i++) {
+		iterations_be = host_to_be32(i);
+		hmac_sha256_vector(kdk, SHA256_MAC_LEN, 3, addr, len, k);
+		k += SHA256_MAC_LEN;
+	}
+
+	if (keys) {
+		k = keys;
+
+		os_memcpy(data->authKey, k, WPS_AUTHKEY_LEN);
+		k += WPS_AUTHKEY_LEN;
+
+		os_memcpy(data->keyWrapKey, k, WPS_KEYWRAPKEY_LEN);
+		k += WPS_KEYWRAPKEY_LEN;
+
+		os_memcpy(data->emsk, k, WPS_EMSK_LEN);
+
+		os_free(keys);
+	}
+
+	return 0;
+}
+
+static int eap_wps_hash(u8 *authKey, u8 *PSK, u8 *PKE, u8 *PKR,
+		u8 *ES,
+		char *dev_passwd, size_t dev_passwd_len,
+		u8 *hash)
+{
+	const u8 *addr[4];
+	size_t	len[4];
+
+	if (os_get_random(ES, WPS_S_NONCE_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for E-Sn");
+		return -1;
+	}
+
+	hmac_sha256(authKey, WPS_AUTHKEY_LEN, (u8 *)dev_passwd,
+			dev_passwd_len, PSK);
+
+	addr[0] = ES;
+	len[0]  = WPS_S_NONCE_LEN;
+	addr[1] = PSK;
+	len[1]  = SHA256_MAC_LEN/2;	/* only use the first 128-bits */
+	addr[2] = PKE;
+	len[2]  = WPS_PK_LEN;
+	addr[3] = PKR;
+	len[3]  = WPS_PK_LEN;
+
+	hmac_sha256_vector(authKey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	return 0;
+}
+
+static int eap_wps_authenticator(u8 *authKey,
+		const u8 *d1, size_t d1_len,
+		u8 *d2, size_t d2_len,
+		u8 *auth)
+{
+	const u8 *addr[2];
+	size_t	len[2];
+	u8		hmac[SHA256_MAC_LEN];
+	size_t	n_vector = 2;
+
+	addr[0] = d1;
+	len[0]  = d1_len;
+	addr[1] = d2;
+	len[1]  = d2_len;
+
+	if (NULL == d2) {
+		n_vector = 1;
+	}
+	hmac_sha256_vector(authKey, WPS_AUTHKEY_LEN, n_vector, addr, len, hmac);
+
+	os_memcpy(auth, hmac, WPS_AUTHENTICATOR_LEN);
+
+	return 0;
+}
+
+/**
+ * eap_wps_authenticator_check - validate authenticator value
+ * @authKey: Pointer to AuthKey
+ * @prv: Pointer to the previous frame (Opcode and Flags not included)
+ * @prv_len: Length of previous frame
+ * @cur: Pointer to the current frame (Opcode and Flags not included)
+ * @cur_len: Length of current frame
+ * @auth: Pointer to the expected authenticator value
+ * Returns: 0 if authenticator value matches, non-zero otherwise
+ *
+ * Calculate the authenticator value for the previous and current
+ * frames and compare it to the expected value.
+ */
+static int eap_wps_authenticator_check(u8 *authKey,
+		const u8 *prv, size_t prv_len,
+		const u8 *cur, size_t cur_len,
+		const u8 *auth)
+{
+	u8 hmac[SHA256_MAC_LEN];
+
+	/*
+	 * Message Authenticator is a keyed hash (HMAC-SHA_256) of (M(n-1) | Mn*)
+	 * using the AuthKey.
+	 * Note that M* should not include the Opcode or Flags
+	 * Also note that the calculation does not include the authenticator
+	 * TLV from the current frame.
+	 */
+	cur_len -= WPS_TLV_LEN + WPS_AUTHENTICATOR_LEN;
+	eap_wps_authenticator(authKey, prv, prv_len, (u8 *)cur, cur_len,
+			hmac);
+
+	return os_memcmp(hmac, auth, WPS_AUTHENTICATOR_LEN);
+}
+
+
+static int eap_wps_encrypted_settings(u8 *authKey, u8 *keyWrapKey,
+		u16 type, u8 *data, size_t data_len,
+		u8 *encrypted)
+{
+	u8 *e = encrypted;
+	u8 authenticator[WPS_KWA_LEN];
+	size_t i;
+	size_t len = 0;
+
+	if (os_get_random(e, WPS_IV_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-WPS: Failed to get random data for IV");
+		return -1;
+	}
+
+	e = eap_wps_write_tlv(e + WPS_IV_LEN, type, data_len, data);
+
+	/*
+	 * Key Wrap Authenticator
+	 */
+	eap_wps_authenticator(authKey, encrypted + WPS_IV_LEN, data_len + WPS_TLV_LEN,
+			NULL, 0, authenticator);
+	e = eap_wps_write_tlv(e, WPS_ELEM_KEY_WRAP_AUTHENTICATOR, WPS_KWA_LEN,
+			authenticator);
+
+	/* Pad out the data re. PKCS #5 V2.0 */
+	for (i=0; i<16; i++) {
+		*e++ = 0x10;
+	}
+
+	len = (size_t)e - (size_t)encrypted - WPS_IV_LEN;
+
+	/* encrypted starts with the IV */
+	aes_128_cbc_encrypt(keyWrapKey, encrypted, encrypted + WPS_IV_LEN, len);
+
+	return 0;
+}
+
+
+static int eap_wps_decrypt_settings(struct eap_wps_data *data, u8 *iv,
+		u8 *encrypted, size_t encrypted_len,
+		u8 *decrypted, size_t *decrypted_len)
+{
+	int ret;
+	u8 pad_len = 0;
+
+	os_memcpy(decrypted, encrypted, encrypted_len);
+	aes_128_cbc_decrypt(data->keyWrapKey, iv, decrypted, encrypted_len);
+
+	/*
+	 * Last byte value in the decrypted data is length of padding at
+	 * end of buffer. The value should be between 1 and 16. Return
+	 * -1 if pad value is out of range. Otherwise adjust the value
+	 *  of decrytped length
+	 */
+	pad_len = *(decrypted + encrypted_len - 1);
+	if (pad_len > 16) {
+		*decrypted_len = 0;
+		ret = -1;
+	} else {
+		*decrypted_len = encrypted_len - pad_len;
+		ret = 0;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "EAP-WPS: decrypted settings", decrypted, *decrypted_len);
+
+	return 0;
+}
diff -urN wpa_supplicant-0.5.10.orig/events.c wpa_supplicant-0.5.10/events.c
--- wpa_supplicant-0.5.10.orig/events.c	2007-11-28 18:49:08.000000000 -0800
+++ wpa_supplicant-0.5.10/events.c	2008-12-03 10:14:39.401798000 -0800
@@ -17,11 +17,13 @@
 #include "common.h"
 #include "eapol_sm.h"
 #include "wpa.h"
+#include "wps.h"
 #include "eloop.h"
 #include "wpa_supplicant.h"
 #include "config.h"
 #include "l2_packet.h"
 #include "wpa_supplicant_i.h"
+#include "wps_i.h"
 #include "pcsc_funcs.h"
 #include "preauth.h"
 #include "pmksa_cache.h"
@@ -350,6 +352,46 @@
 }
 
 
+static struct wpa_ssid *
+wpa_supplicant_wps_select_wpa_ssid(struct wpa_supplicant *wpa_s,
+		struct wpa_ssid *group, struct wpa_scan_result *bss)
+{
+	struct wpa_ssid *ssid, *selected_ssid = NULL;
+
+	for (ssid = group; ssid; ssid = ssid->pnext) {
+		if (ssid->disabled) {
+			wpa_printf(MSG_DEBUG, "   skip - disabled");
+			continue;
+		}
+
+		if ((ssid->proto & WPA_PROTO_WPS) && bss->wps_ie_len > 0) {
+			selected_ssid = ssid;
+			os_memcpy(ssid->own_addr, wpa_s->own_addr, ETH_ALEN);
+			if (ssid->ssid) {
+				os_free(ssid->ssid);
+			}
+			if (NULL == (ssid->ssid = os_malloc(bss->ssid_len))) {
+				wpa_printf(MSG_ERROR, "EAP-WPS: Cannot allocate SSID");
+				break;
+			}
+			os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+			ssid->ssid_len = bss->ssid_len;
+			wpa_printf(MSG_DEBUG, "   selected WPS AP "
+				   MACSTR " ssid='%s'",
+				   MAC2STR(bss->bssid),
+				   wpa_ssid_txt(bss->ssid,
+						bss->ssid_len));
+
+			if (NULL != ssid->identity)
+				os_free(ssid->identity);
+			ssid->identity = (u8 *)os_strdup(WPS_EAP_IDENTITY);
+			ssid->identity_len = os_strlen(WPS_EAP_IDENTITY);
+		}
+	}
+
+	return selected_ssid;
+}
+
 static struct wpa_scan_result *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
 			  struct wpa_scan_result *results, int num,
@@ -358,6 +400,12 @@
 	struct wpa_ssid *ssid;
 	struct wpa_scan_result *bss, *selected = NULL;
 	int i;
+#ifdef EAP_WPS
+	struct wps_ie_data wps_data;
+	struct wpa_scan_result **wps_results = NULL;
+	size_t wps_results_len = 0;
+	void *tmp;
+#endif
 	struct wpa_blacklist *e;
 
 	wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
@@ -365,7 +413,158 @@
 
 	bss = NULL;
 	ssid = NULL;
-	/* First, try to find WPA-enabled AP */
+#ifdef EAP_WPS
+	if (WPS_CONFIG_METHOD_NONE != wpa_s->wps_config_method) {
+		int pbc_count = 0;		/* Number of devices using PBC */
+		u8 selected_uuid[WPS_UUID_LEN];
+
+		/* First, try to find WPS-enabled AP */
+		wpa_printf(MSG_DEBUG, "Try to find WPS-selected AP");
+		for (i = 0; i < num; i++) {
+			bss = &results[i];
+			wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+				   "wps_ie_len=%lu caps=0x%x",
+				   i, MAC2STR(bss->bssid),
+				   wpa_ssid_txt(bss->ssid, bss->ssid_len),
+				   (unsigned long) bss->wps_ie_len,
+				   bss->caps);
+
+			if (0 == bss->wps_ie_len) {
+				wpa_printf(MSG_DEBUG, "   skip - no WPS IE");
+				continue;
+			}
+
+			wps_results_len++;
+			tmp = wps_results;
+			wps_results = os_realloc(wps_results,
+					wps_results_len * sizeof(struct wpa_scan_result *));
+			if (NULL == wps_results) {
+				os_free(tmp);
+				wpa_printf(MSG_ERROR, "%s: can't realloc wps_results", __func__);
+				return NULL;
+			}
+
+			wps_results[wps_results_len - 1] = bss;
+
+			os_memset(&wps_data, 0, sizeof(struct wps_ie_data));
+
+			if (wpa_parse_wps_ie(bss->wps_ie, bss->wps_ie_len, &wps_data)) {
+				wpa_printf(MSG_DEBUG, "   skip - bad WPS IE");
+				continue;
+			}
+
+			/*
+			 * Selected Registrar is an optional element which if TRUE
+			 * indicates Device Password ID is present and valid.
+			 *
+			 * Note that some external registrars like ********* (removed
+			 * to protect the bone-headed) won't cause the AP To set
+			 * Selected Registrar to TRUE until after an enrollee has
+			 * attempted to start the WPS protocol (See below for a
+			 * work around)
+			 */
+			if (FALSE == wps_data.selected_registrar) {
+				wpa_printf(MSG_DEBUG, "   skip - registrar not selected");
+				continue;
+			}
+
+			switch (wpa_s->wps_config_method) {
+			case WPS_CONFIG_METHOD_PBC:
+				if (wps_data.device_password_id == WPS_DEVICE_PASSWORD_ID_PBC) {
+					pbc_count++;
+					if (1 == pbc_count) {
+						wpa_printf(MSG_DEBUG, "   pending PBC");
+						os_memcpy(selected_uuid, wps_data.uuid, WPS_UUID_LEN);
+					} else if (0 == os_memcmp(selected_uuid, wps_data.uuid, WPS_UUID_LEN)) {
+						pbc_count--;
+						wpa_printf(MSG_DEBUG, "   skip - matching UUID_E PBC");
+						continue;
+					} else {
+						wpa_printf(MSG_DEBUG, "   skip - overlapping PBC");
+						continue;
+					}
+				} else {
+					wpa_printf(MSG_DEBUG, "   skip - PBC not activated");
+					continue;
+				}
+				break;
+			case WPS_CONFIG_METHOD_DISPLAY:
+			case WPS_CONFIG_METHOD_LABEL:
+				if (wps_data.device_password_id != WPS_DEVICE_PASSWORD_ID_PIN) {
+					wpa_printf(MSG_DEBUG, "   skip - PIN not activated");
+					continue;
+				}
+				break;
+			default:
+				wpa_printf(MSG_DEBUG, "   skip - WPS not configured");
+				continue;
+			}
+
+			e = wpa_blacklist_get(wpa_s, bss->bssid);
+			if (e && e->count > 1) {
+				wpa_printf(MSG_DEBUG, "   skip - blacklisted");
+				continue;
+			}
+
+			*selected_ssid = wpa_supplicant_wps_select_wpa_ssid(wpa_s,
+					group, bss);
+			if (NULL != *selected_ssid) {
+				selected = bss;
+
+				if (WPS_CONFIG_METHOD_PBC != wpa_s->wps_config_method) {
+					break;
+				}
+			}
+		}
+
+		/*
+		 * If no AP was selected but one or more AP advertised a WPS IE,
+		 * run the WPS protocol against each AP in case one has an
+		 * "ill behaved" external registrar
+		 */
+		if (!selected && (WPS_CONFIG_METHOD_PBC != wpa_s->wps_config_method)) {
+			pbc_count = 0;
+			wpa_printf(MSG_DEBUG, "Try to find WPS-capable AP");
+			for (i = 0; i < wps_results_len; i++) {
+				bss = wps_results[i];
+				wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' ",
+						i, MAC2STR(bss->bssid),
+						wpa_ssid_txt(bss->ssid, bss->ssid_len));
+
+				e = wpa_blacklist_get(wpa_s, bss->bssid);
+				if (e && e->count > 1) {
+					wpa_printf(MSG_DEBUG, "   skip - blacklisted");
+					continue;
+				}
+
+				*selected_ssid = wpa_supplicant_wps_select_wpa_ssid(wpa_s,
+						group, bss);
+				if (NULL != *selected_ssid) {
+					selected = bss;
+
+					if (WPS_CONFIG_METHOD_PBC != wpa_s->wps_config_method) {
+						break;
+					}
+				}
+			}
+		}
+
+		if ((WPS_CONFIG_METHOD_PBC == wpa_s->wps_config_method) &&
+				(pbc_count > 1)) {
+			selected = NULL;
+			*selected_ssid = NULL;
+			ssid = NULL;
+			bss = NULL;
+
+			wps_cancel_walktime_timeout(wpa_s);
+			wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_WPS_OVERLAP);
+		}
+
+		os_free(wps_results);
+	}
+#endif
+
+	/* Next, try to find WPA-enabled AP */
 	wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP");
 	for (i = 0; i < num && !selected; i++) {
 		bss = &results[i];
diff -urN wpa_supplicant-0.5.10.orig/Makefile wpa_supplicant-0.5.10/Makefile
--- wpa_supplicant-0.5.10.orig/Makefile	2008-02-19 16:08:52.000000000 -0800
+++ wpa_supplicant-0.5.10/Makefile	2008-09-03 10:03:58.000000000 -0700
@@ -602,6 +602,26 @@
 CONFIG_INTERNAL_MD5=y
 endif
 
+ifdef CONFIG_EAP_WPS
+# EAP-WPS
+ifeq ($(CONFIG_EAP_WPS), dyn)
+EAPDYN += eap_wps.so
+else
+OBJS += eap_wps.o
+endif
+CFLAGS += -DEAP_WPS
+NEED_SHA256=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_INTERNAL_DES=y
+
+ifneq ($(TLS_FUNCS), y)
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL -DCONFIG_INTERNAL_LIBTOMMATH -DEAP_TLS_FUNCS -DCONFIG_TLS_INTERNAL
+OBJS += asn1.o crypto_internal.o rsa.o bignum.o
+endif
+
+endif
+
 ifdef CONFIG_INTERNAL_AES
 CFLAGS += -DINTERNAL_AES
 endif
@@ -712,7 +732,7 @@
 endif
 
 ifndef CONFIG_NO_WPA
-OBJS += wpa.o preauth.o pmksa_cache.o
+OBJS += wpa.o preauth.o pmksa_cache.o wps.o
 NEED_AES=y
 else
 CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2
diff -urN wpa_supplicant-0.5.10.orig/wpa_cli.c wpa_supplicant-0.5.10/wpa_cli.c
--- wpa_supplicant-0.5.10.orig/wpa_cli.c	2008-02-17 10:26:11.000000000 -0800
+++ wpa_supplicant-0.5.10/wpa_cli.c	2008-09-03 10:04:48.000000000 -0700
@@ -131,6 +131,9 @@
 "  ap_scan <value> = set ap_scan parameter\n"
 "  stkstart <addr> = request STK negotiation with <addr>\n"
 "  terminate = terminate wpa_supplicant\n"
+"  pbc = start Push Button Configuration\n"
+"  pin_get = get the PIN for this device\n"
+"  pin_entered = PIN has been entered into registrar\n"
 "  quit = exit wpa_cli\n";
 
 static struct wpa_ctrl *ctrl_conn;
@@ -412,6 +415,27 @@
 }
 
 
+static int wpa_cli_cmd_pbc(struct wpa_ctrl *ctrl, int argc,
+				char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PBC");
+}
+
+
+static int wpa_cli_cmd_pin_get(struct wpa_ctrl *ctrl, int argc,
+				char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PIN_GET");
+}
+
+
+static int wpa_cli_cmd_pin_entered(struct wpa_ctrl *ctrl, int argc,
+				char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PIN_ENTERED");
+}
+
+
 static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -1021,6 +1045,9 @@
 	{ "interface_remove", wpa_cli_cmd_interface_remove },
 	{ "ap_scan", wpa_cli_cmd_ap_scan },
 	{ "stkstart", wpa_cli_cmd_stkstart },
+	{ "pbc", wpa_cli_cmd_pbc },
+	{ "pin_get", wpa_cli_cmd_pin_get },
+	{ "pin_entered", wpa_cli_cmd_pin_entered },
 	{ NULL, NULL }
 };
 
diff -urN wpa_supplicant-0.5.10.orig/wpa_ctrl.h wpa_supplicant-0.5.10/wpa_ctrl.h
--- wpa_supplicant-0.5.10.orig/wpa_ctrl.h	2007-03-24 19:09:50.000000000 -0700
+++ wpa_supplicant-0.5.10/wpa_ctrl.h	2008-07-21 15:39:03.000000000 -0700
@@ -47,6 +47,10 @@
 /** EAP authentication failed (EAP-Failure received) */
 #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
 
+#define WPA_EVENT_WPS_ERROR "CTRL-EVENT-WPS-ERROR "
+#define WPA_EVENT_WPS_OVERLAP "CTRL-EVENT-WPS-OVERLAP "
+#define WPA_EVENT_WPS_SUCCESS "CTRL-EVENT-WPS-SUCCESS "
+
 
 /* wpa_supplicant/hostapd control interface access */
 
diff -urN wpa_supplicant-0.5.10.orig/wpa_supplicant.c wpa_supplicant-0.5.10/wpa_supplicant.c
--- wpa_supplicant-0.5.10.orig/wpa_supplicant.c	2008-02-17 10:42:02.000000000 -0800
+++ wpa_supplicant-0.5.10/wpa_supplicant.c	2008-09-03 09:48:40.000000000 -0700
@@ -22,11 +22,13 @@
 #include "eapol_sm.h"
 #include "eap.h"
 #include "wpa.h"
+#include "wps.h"
 #include "eloop.h"
 #include "wpa_supplicant.h"
 #include "config.h"
 #include "l2_packet.h"
 #include "wpa_supplicant_i.h"
+#include "wps_i.h"
 #include "ctrl_iface.h"
 #include "ctrl_iface_dbus.h"
 #include "pcsc_funcs.h"
@@ -359,7 +361,21 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
-	if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+	if (wpa_s->wps_config_method) {
+		if (wpa_s->current_ssid && (wpa_s->current_ssid->assoc_state ==
+					WPS_ASSOCIATION_STATE_CONNECTION_SUCCESS)) {
+			wps_cancel_walktime_timeout(wpa_s);
+			wpa_supplicant_cancel_auth_timeout(wpa_s);
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_WPS_SUCCESS);
+		} else {
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_WPS_ERROR);
+		}
+
+		wpa_supplicant_deauthenticate(wpa_s, REASON_UNSPECIFIED);
+		wpa_s->reassociate = 1;
+
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
 		wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE);
 	} else {
 		eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
@@ -606,6 +622,9 @@
 			eapol_conf.required_keys = 0;
 		}
 	}
+	if (wpa_s->wps_config_method) {
+		eapol_conf.expect_fail = 1;
+	}
 	if (wpa_s->conf)
 		eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
 	eapol_conf.workaround = ssid->eap_workaround;
@@ -710,6 +729,8 @@
 	wpa_supplicant_cancel_auth_timeout(wpa_s);
 
 	ieee80211_sta_deinit(wpa_s);
+
+	os_free(wpa_s->wps_pin);
 }
 
 
@@ -961,6 +982,9 @@
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct wpa_ssid *ssid;
 	int enabled, scan_req = 0, ret;
+	u8 wps_ie[SSID_MAX_WPS_IE_LEN];
+	size_t wps_ie_len;
+	const char *uuid_str = NULL;
 
 	if (wpa_s->disconnected && !wpa_s->scan_req)
 		return;
@@ -970,6 +994,7 @@
 	while (ssid) {
 		if (!ssid->disabled) {
 			enabled++;
+			uuid_str = ssid->uuid_e;
 			break;
 		}
 		ssid = ssid->next;
@@ -1056,10 +1081,21 @@
 		return;
 	}
 
+	if (WPS_CONFIG_METHOD_NONE != wpa_s->wps_config_method) {
+		if (wpa_create_wps_ie(uuid_str, wpa_s->wps_config_method, wps_ie,
+					&wps_ie_len)) {
+			wpa_printf(MSG_WARNING, "WPA: Failed to create WPS IE");
+			return;
+		}
+	} else {
+		wps_ie_len = 0;
+	}
+
 	if (wpa_s->use_client_mlme) {
 		ret = ieee80211_sta_req_scan(wpa_s, ssid ? ssid->ssid : NULL,
 					     ssid ? ssid->ssid_len : 0);
 	} else {
+		wpa_drv_set_probe_req_ie(wpa_s, wps_ie, wps_ie_len);
 		ret = wpa_drv_scan(wpa_s, ssid ? ssid->ssid : NULL,
 				   ssid ? ssid->ssid_len : 0);
 	}
@@ -1339,6 +1375,8 @@
 	int wep_keys_set = 0;
 	struct wpa_driver_capa capa;
 	int assoc_failed = 0;
+	u8 wps_ie[SSID_MAX_WPS_IE_LEN];
+	size_t wps_ie_len;
 
 	wpa_s->reassociate = 0;
 	if (bss) {
@@ -1382,7 +1420,19 @@
 	}
 	wpa_drv_set_auth_alg(wpa_s, algs);
 
-	if (bss && (bss->wpa_ie_len || bss->rsn_ie_len) &&
+	if ((ssid->proto & WPA_PROTO_WPS) && bss && bss->wps_ie_len) {
+		cipher_pairwise = WPA_CIPHER_NONE;
+		cipher_group    = WPA_CIPHER_NONE;
+
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+		wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_WPS);
+
+		if (wpa_s->wps_pin) {
+			ssid->wps_pin = os_strdup(wpa_s->wps_pin);
+		}
+	} else if (bss && (bss->wpa_ie_len || bss->rsn_ie_len) &&
 	    (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) {
 		int try_opportunistic;
 		try_opportunistic = ssid->proactive_key_caching &&
@@ -1497,6 +1547,18 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
+	if (WPS_CONFIG_METHOD_NONE != wpa_s->wps_config_method) {
+		if (wpa_create_wps_ie(ssid->uuid_e, wpa_s->wps_config_method,
+					wps_ie, &wps_ie_len)) {
+			wpa_printf(MSG_WARNING, "WPA: Failed to create WPS IE");
+			return;
+		}
+	} else {
+		wps_ie_len = 0;
+	}
+	params.wps_ie = wps_ie;
+	params.wps_ie_len = wps_ie_len;
+
 	if (wpa_s->use_client_mlme)
 		ret = ieee80211_sta_associate(wpa_s, &params);
 	else
diff -urN wpa_supplicant-0.5.10.orig/wpa_supplicant.conf wpa_supplicant-0.5.10/wpa_supplicant.conf
--- wpa_supplicant-0.5.10.orig/wpa_supplicant.conf	2007-12-27 15:59:35.000000000 -0800
+++ wpa_supplicant-0.5.10/wpa_supplicant.conf	2008-08-01 14:19:10.000000000 -0700
@@ -740,6 +740,11 @@
 SGVsbG8gV29ybGQhCg==
 }
 
+# Example of EAP-WPS (aka Wifi Protected Setup)
+network={
+	proto=WPS
+	eap=WPS
+}
 
 # Wildcard match for SSID (plaintext APs only). This example select any
 # open AP regardless of its SSID.
diff -urN wpa_supplicant-0.5.10.orig/wpa_supplicant_i.h wpa_supplicant-0.5.10/wpa_supplicant_i.h
--- wpa_supplicant-0.5.10.orig/wpa_supplicant_i.h	2007-12-27 16:50:15.000000000 -0800
+++ wpa_supplicant-0.5.10/wpa_supplicant_i.h	2008-09-03 09:46:41.000000000 -0700
@@ -342,6 +342,9 @@
 
 	struct wpa_client_mlme mlme;
 	int use_client_mlme;
+
+	int wps_config_method;
+	char *wps_pin;
 };
 
 
@@ -693,4 +696,14 @@
 	return -1;
 }
 
+static inline int wpa_drv_set_probe_req_ie(struct wpa_supplicant *wpa_s,
+					  u8 *ie, size_t ie_len)
+{
+	if (wpa_s->driver->set_probe_req_ie) {
+		return wpa_s->driver->set_probe_req_ie(wpa_s->drv_priv, ie,
+				ie_len);
+	}
+	return -1;
+}
+
 #endif /* WPA_SUPPLICANT_I_H */
diff -urN wpa_supplicant-0.5.10.orig/wps.c wpa_supplicant-0.5.10/wps.c
--- wpa_supplicant-0.5.10.orig/wps.c	1969-12-31 16:00:00.000000000 -0800
+++ wpa_supplicant-0.5.10/wps.c	2008-08-29 17:15:31.000000000 -0700
@@ -0,0 +1,291 @@
+/*
+ * WiFi Protected Setup - WPS specific processing
+ * Copyright (c) 2008, Chuck Tuffli <chuck@tuffli.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "wps.h"
+
+static const int WPS_SELECTOR_LEN = 4;
+static const u8 WPS_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 0x4 };
+static const u16 WPS_VERSION = 0x10;
+
+/* WPS IE version 1.0
+ *
+ * Required fields for all IE
+ * 00-50-f2:04 (OUI:OUI type)
+ * 0x10 (version)
+ * 0x01 0x02 (wiFi Protected Setup State; unconfigured, configured)
+ * (following fields are optional:)
+ * (AP Setup Locked; BOOL )
+ * (Selected Registrar; BOOL)
+ * (Device Password ID; )
+ * (Selected Registrar Config Methods; )
+ * (following fields are optional in some management packets)
+ * (Response Type; required=Probe Resp)
+ * (UUID-R; optional=Beacon, required=Probe Resp)
+ * (Manufacturer; required=Probe Resp)
+ * (Model Name; required=Probe Resp)
+ * (Model Number; required=Probe Resp)
+ * (Serial Number; required=Probe Resp)
+ * (Primary Device Type; required=Probe Resp)
+ * (Device Name; required=Probe Resp)
+ * (Config Methodes; required=Probe Resp)
+ * (RF Bands; optional=Beacon and Probe Resp)
+ */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct wps_ie_hdr {
+	u8 elem_id;
+	u8 len;
+	u8 oui[3];
+	u8 oui_type;
+	u16 type_version;
+	u16 len_version;
+	u8 version;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+int wpa_parse_wps_ie(const u8 *wps_ie, size_t wps_ie_len,
+		     struct wps_ie_data *data)
+{
+	const struct wps_ie_hdr *hdr;
+	const u8 *pos;
+	int left;
+	u16 elem, len;
+
+	data->proto = WPA_PROTO_WPS;
+
+	if (0 == wps_ie_len) {
+		/* No WPS IE - fail silently */
+		return -1;
+	}
+
+	if (wps_ie_len < sizeof(struct wps_ie_hdr)) {
+		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+			   __func__, (unsigned long) wps_ie_len);
+		return -1;
+	}
+
+	hdr = (const struct wps_ie_hdr *) wps_ie;
+
+	if (hdr->elem_id != GENERIC_INFO_ELEM ||
+	    hdr->len != wps_ie_len - 2 ||
+	    os_memcmp(hdr->oui, WPS_OUI_TYPE, WPS_SELECTOR_LEN) != 0 ||
+	    hdr->version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+			   __func__);
+		return -1;
+	}
+
+	pos = (const u8 *) (hdr + 1);
+	left = wps_ie_len - sizeof(*hdr);
+
+	while (left >= 2) {
+		elem = WPA_GET_BE16(pos);
+		len  = WPA_GET_BE16(pos+2);
+		pos  += 4;
+		left -= 4;
+
+		if ((0 == len) || (len > left)) {
+			wpa_printf(MSG_DEBUG, "%s: bad length %d (left %d)\n",
+					__func__, len, left);
+			return -1;
+		}
+		switch (elem) {
+		case WPS_ELEM_DEVICE_PASSWORD_ID:
+			if (data->selected_registrar)
+				data->device_password_id = WPA_GET_BE16(pos);
+			break;
+		case WPS_ELEM_CONFIG_METHODS:
+			data->config_method = WPA_GET_BE16(pos);
+			break;
+		case WPS_ELEM_SELECTED_REGISTRAR:
+			data->selected_registrar = *pos;
+			break;
+		case WPS_ELEM_UUID_E:
+			os_memcpy(data->uuid, pos, sizeof(data->uuid));
+			break;
+		}
+		pos  += len;
+		left -= len;
+	}
+	return 0;
+}
+
+static u8 * wps_write_tlv(u8 *buf, u16 type, u16 len, u8 *val)
+{
+	WPA_PUT_BE16(buf, type);
+	WPA_PUT_BE16(buf + 2, len);
+	os_memcpy(buf + 4, val, len);
+
+	return (buf + 4 + len);
+}
+
+static inline u8 * wps_write_tlv_u8(u8 *buf, u16 type, u8 val)
+{
+	return wps_write_tlv(buf, type, 1, &val);
+}
+
+static inline u8 * wps_write_tlv_u16(u8 *buf, u16 type, u16 val)
+{
+	u16 be_val = host_to_be16(val);
+
+	return wps_write_tlv(buf, type, 2, (u8 *)&be_val);
+}
+
+int wpa_create_wps_ie(const char *uuid_str, u16 method, u8 *wps_ie,
+		size_t *wps_ie_len)
+{
+	u8	*pos = wps_ie;
+	u8 uuid_e[WPS_UUID_LEN];
+
+	*wps_ie_len = 0;
+
+	if (hexstr2bin(uuid_str, uuid_e, WPS_UUID_LEN)) {
+		wpa_printf(MSG_ERROR, "EAP-WPS: bad UUID format %s", uuid_str);
+		return -1;
+	}
+
+	*pos++ = GENERIC_INFO_ELEM;
+	pos++;	/* skip length initially */
+	os_memcpy(pos, WPS_OUI_TYPE, WPS_SELECTOR_LEN);
+	pos += WPS_SELECTOR_LEN;
+	pos = wps_write_tlv_u8(pos, WPS_ELEM_VERSION, WPS_VERSION);
+	pos = wps_write_tlv_u8(pos, WPS_ELEM_REQUEST_TYPE,
+			WPS_REQUEST_TYPE_ENROLLEE_OPEN);
+	pos = wps_write_tlv_u16(pos, WPS_ELEM_CONFIG_METHODS,
+			WPS_CONFIG_METHOD_LABEL |
+			WPS_CONFIG_METHOD_DISPLAY |
+			WPS_CONFIG_METHOD_PBC);
+	pos = wps_write_tlv(pos, WPS_ELEM_UUID_E, WPS_UUID_LEN, uuid_e);
+	pos = wps_write_tlv(pos, WPS_ELEM_PRIMARY_DEVICE_TYPE, 8,
+			(u8 *)"\x00\x01\x00\x50\xf2\x04\x00\x01");
+	pos = wps_write_tlv_u8(pos, WPS_ELEM_RF_BANDS,
+			WPS_RF_BAND_2_4GHZ | WPS_RF_BAND_5_0GHZ);
+	pos = wps_write_tlv_u16(pos, WPS_ELEM_ASSOCIATION_STATE,
+			WPS_ASSOCIATION_STATE_NOT_ASSOCIATED);
+	pos = wps_write_tlv_u16(pos, WPS_ELEM_CONFIGURATION_ERROR,
+			WPS_CONFIGURATION_ERROR_NO_ERROR);
+	pos = wps_write_tlv_u16(pos, WPS_ELEM_DEVICE_PASSWORD_ID,
+			WPS_CONFIG_METHOD_PBC == method ?
+			WPS_DEVICE_PASSWORD_ID_PBC : WPS_DEVICE_PASSWORD_ID_PIN);
+
+	*wps_ie_len = (size_t)pos - (size_t)wps_ie;
+
+	wps_ie[1] = (u8)(*wps_ie_len) - 2;
+
+	return 0;
+}
+
+static void wps_walktime_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "WPS PBC method timed out");
+	wpa_s->wps_config_method = 0;
+}
+
+int wps_config_pbc(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->wps_config_method = WPS_CONFIG_METHOD_PBC;
+
+	os_free(wpa_s->wps_pin);
+	wpa_s->wps_pin = os_strdup(WPS_PIN_PBC);
+
+	eloop_cancel_timeout(wps_walktime_timeout, wpa_s, NULL);
+	eloop_register_timeout(WPS_WALK_TIME, 0, wps_walktime_timeout, wpa_s, NULL);
+
+	return 0;
+}
+
+int wps_cancel_walktime_timeout(struct wpa_supplicant *wpa_s)
+{
+	eloop_cancel_timeout(wps_walktime_timeout, wpa_s, NULL);
+	wpa_s->wps_config_method = 0;
+
+	return 0;
+}
+
+/**
+ * wps_generate_pin - Generate a Device Password (aka PIN)
+ *
+ * Creates an 8 digit pin by generating a 7 digit random number
+ * and appending a checksum digit to the end of the random number
+ * (WPS Section 6.4.1)
+ */
+static u32 wps_generate_pin()
+{
+	u32 rand;
+	u32 pin;
+	u32 accum = 0;
+
+#define PIN_MIN 1
+#define PIN_MAX 9999999
+
+	os_get_random((unsigned char *)&rand, sizeof(rand));
+	pin = PIN_MIN + (rand / ((u32)(-1) / PIN_MAX + 1));
+
+	pin *= 10;
+	accum += 3 * ((pin / 10000000) % 10);
+	accum += 1 * ((pin / 1000000) % 10);
+	accum += 3 * ((pin / 100000) % 10);
+	accum += 1 * ((pin / 10000) % 10);
+	accum += 3 * ((pin / 1000) % 10);
+	accum += 1 * ((pin / 100) % 10);
+	accum += 3 * ((pin / 10) % 10);
+	
+	pin += (10 - (accum % 10)) % 10;
+
+	return pin;
+}
+
+int wps_get_pin(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	u32 pin;
+
+	os_free(wpa_s->wps_pin);
+	if (NULL == (wpa_s->wps_pin = os_malloc(sizeof(WPS_PIN_PBC)))) {
+		wpa_msg(wpa_s, MSG_ERROR, "WPS PIN method no memory");
+		return -1;
+	}
+
+	pin = wps_generate_pin();
+	os_snprintf(wpa_s->wps_pin, sizeof(WPS_PIN_PBC), "%08d", pin);
+
+	os_snprintf(buf, buflen, "%08d", pin);
+	buf[buflen - 1] = '\0';
+
+	return os_strlen(buf);
+}
+
+int wps_config_pin(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->wps_config_method = WPS_CONFIG_METHOD_DISPLAY;
+
+	eloop_cancel_timeout(wps_walktime_timeout, wpa_s, NULL);
+	eloop_register_timeout(WPS_WALK_TIME, 0, wps_walktime_timeout, wpa_s, NULL);
+
+	return 0;
+}
diff -urN wpa_supplicant-0.5.10.orig/wps.h wpa_supplicant-0.5.10/wps.h
--- wpa_supplicant-0.5.10.orig/wps.h	1969-12-31 16:00:00.000000000 -0800
+++ wpa_supplicant-0.5.10/wps.h	2008-08-14 11:51:44.000000000 -0700
@@ -0,0 +1,269 @@
+/*
+ * WiFi Protected Setup - WPS definitions
+ * Copyright (c) 2008, Chuck Tuffli <chuck@tuffli.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+#include "defs.h"
+
+#ifndef BIT
+#define BIT(n) (1 << (n))
+#endif
+
+#define WPS_UUID_LEN 16
+
+struct wps_ie_data {
+	int proto;
+	int device_password_id;
+	int config_method;
+	Boolean selected_registrar;
+	u8 uuid[WPS_UUID_LEN];
+};
+
+/**
+ * WPS Op-Codes
+ */
+#define WPS_OPCODE_WSC_Start	0x01
+#define WPS_OPCODE_WSC_ACK		0x02
+#define WPS_OPCODE_WSC_NACK		0x03
+#define WPS_OPCODE_WSC_MSG		0x04
+#define WPS_OPCODE_WSC_Done		0x05
+#define WPS_OPCODE_WSC_FRAG_ACK	0x06
+
+/**
+ * WPS EAP Flags
+ * MF = more fragments
+ * LF = length field
+ */
+#define WPS_EAP_FLAG_MF			0x01
+#define WPS_EAP_FLAG_LF			0x02
+
+#define WPS_ELEM_AP_CHANNEL				0x1001
+#define WPS_ELEM_ASSOCIATION_STATE		0x1002
+#define WPS_ELEM_AUTHENTICATION_TYPE	0x1003
+#define WPS_ELEM_AUTHENTICATION_TYPE_FLAG	0x1004
+#define WPS_ELEM_AUTHENTICATOR			0x1005
+#define WPS_ELEM_CONFIG_METHODS			0x1008
+#define WPS_ELEM_CONFIGURATION_ERROR	0x1009
+#define WPS_ELEM_CONFIRMATION_URL4		0x100a
+#define WPS_ELEM_CONFIRMATION_URL6		0x100b
+#define WPS_ELEM_CONNECTION_TYPE		0x100c
+#define WPS_ELEM_CONNECTION_TYPE_FLAG	0x100d
+#define WPS_ELEM_CREDENTIAL				0x100e
+#define WPS_ELEM_DEVICE_NAME			0x1011
+#define WPS_ELEM_DEVICE_PASSWORD_ID		0x1012
+#define WPS_ELEM_E_HASH1				0x1014
+#define WPS_ELEM_E_HASH2				0x1015
+#define WPS_ELEM_E_SNONCE1				0x1016
+#define WPS_ELEM_E_SNONCE2				0x1017
+#define WPS_ELEM_ENCRYPTED_SETTINGS		0x1018
+#define WPS_ELEM_ENCRYPTION_TYPE		0x100f
+#define WPS_ELEM_ENCRYPTION_TYPE_FLAG	0x1010
+#define WPS_ELEM_ENROLLEE_NONCE			0x101a
+#define WPS_ELEM_FEATURE_ID				0x101b
+#define WPS_ELEM_IDENTITY				0x101c
+#define WPS_ELEM_IDENTITY_PROOF			0x101d
+#define WPS_ELEM_KEY_WRAP_AUTHENTICATOR	0x101e
+#define WPS_ELEM_KEY_IDENTIFIER			0x101f
+#define WPS_ELEM_MAC_ADDRESS			0x1020
+#define WPS_ELEM_MANUFACTURER			0x1021
+#define WPS_ELEM_MESSAGE_TYPE			0x1022
+#define WPS_ELEM_MODEL_NAME				0x1023
+#define WPS_ELEM_MODEL_NUMBER			0x1024
+#define WPS_ELEM_NETWORK_INDEX			0x1026
+#define WPS_ELEM_NETWORK_KEY			0x1027
+#define WPS_ELEM_NETWORK_KEY_INDEX		0x1028
+#define WPS_ELEM_NEW_DEVICE_NAME		0x1029
+#define WPS_ELEM_NEW_PASSWORD			0x102a
+#define WPS_ELEM_OOB_DEVICE_PASSWORD	0x102c
+#define WPS_ELEM_OS_VERSION				0x102d
+#define WPS_ELEM_POWER_LEVEL			0x102f
+#define WPS_ELEM_PSK_CURRENT			0x1030
+#define WPS_ELEM_PSK_MAX				0x1031
+#define WPS_ELEM_PUBLIC_KEY				0x1032
+#define WPS_ELEM_RADIO_ENABLED			0x1033
+#define WPS_ELEM_REBOOT					0x1034
+#define WPS_ELEM_REGISTRAR_CURRENT		0x1035
+#define WPS_ELEM_REGISTRAR_ESTABLISHED	0x1036
+#define WPS_ELEM_REGISTRAR_LIST			0x1037
+#define WPS_ELEM_REGISTRAR_MAX			0x1038
+#define WPS_ELEM_REGISTRAR_NONCE		0x1039
+#define WPS_ELEM_REQUEST_TYPE			0x103a
+#define WPS_ELEM_RESPONSE_TYPE			0x103b
+#define WPS_ELEM_RF_BANDS				0x103c
+#define WPS_ELEM_R_HASH1				0x103d
+#define WPS_ELEM_R_HASH2				0x103e
+#define WPS_ELEM_R_SNONCE1				0x103f
+#define WPS_ELEM_R_SNONCE2				0x1040
+#define WPS_ELEM_SELECTED_REGISTRAR		0x1041
+#define WPS_ELEM_SERIAL_NUMBER			0x1042
+#define WPS_ELEM_WPS_STATE				0x1044
+#define WPS_ELEM_SSID					0x1045
+#define WPS_ELEM_TOTAL_NETWORKS			0x1046
+#define WPS_ELEM_UUID_E					0x1047
+#define WPS_ELEM_UUID_R					0x1048
+#define WPS_ELEM_VENDOR_EXTENSION		0x1049
+#define WPS_ELEM_VERSION				0x104a
+#define WPS_ELEM_X509_CERT_REQUEST		0x104b
+#define WPS_ELEM_X509_CERTIFICATE		0x104c
+#define WPS_ELEM_EAP_IDENTITY			0x104d
+#define WPS_ELEM_MESSAGE_COUNTER		0x104e
+#define WPS_ELEM_PUBLIC_KEY_HASH		0x104f
+#define WPS_ELEM_REKEY_KEY				0x1050
+#define WPS_ELEM_KEY_LIFETIME			0x1051
+#define WPS_ELEM_PERMITTED_CONFIG_METHODS	0x1052
+#define WPS_ELEM_SELECTED_REGISTRAR_CONFIG_METHODS	0x1053
+#define WPS_ELEM_PRIMARY_DEVICE_TYPE	0x1054
+#define WPS_ELEM_SECONDARY_DEVICE_TYPE_LIST	0x1055
+#define WPS_ELEM_PORTABLE_DEVICE		0x1056
+#define WPS_ELEM_AP_SETUP_LOCKED		0x1057
+#define WPS_ELEM_APPLICATION_EXTENSION	0x1058
+#define WPS_ELEM_EAP_TYPE				0x1059
+#define WPS_ELEM_INITIALIZATION_VECTOR	0x1060
+#define WPS_ELEM_KEY_PROVIDED_AUTOMATICALLY	0x1061
+#define WPS_ELEM_8021X_ENABLED			0x1062
+#define WPS_ELEM_APPSESSIONKEY			0x1063
+#define WPS_ELEM_WEPTRANSMITKEY			0x1064
+
+
+#define WPS_ASSOCIATION_STATE_NOT_ASSOCIATED		0x0000
+#define WPS_ASSOCIATION_STATE_CONNECTION_SUCCESS	0x0001
+#define WPS_ASSOCIATION_STATE_CONNECTION_FAILURE	0x0002
+#define WPS_ASSOCIATION_STATE_ASSOCIATION_FAILURE	0x0003
+#define WPS_ASSOCIATION_STATE_IP_FAILURE			0x0004
+
+#define WPS_AUTHENTICATION_TYPE_OPEN	0x0001
+#define WPS_AUTHENTICATION_TYPE_WPAPSK	0x0002
+#define WPS_AUTHENTICATION_TYPE_Shared	0x0004
+#define WPS_AUTHENTICATION_TYPE_WPA		0x0008
+#define WPS_AUTHENTICATION_TYPE_WPA2	0x0010
+#define WPS_AUTHENTICATION_TYPE_WPA2PSK	0x0020
+
+#define WPS_CONFIG_METHOD_NONE			0x0000		/* NB not defined by spec */
+#define WPS_CONFIG_METHOD_USBA			0x0001
+#define WPS_CONFIG_METHOD_ETHERNET		0x0002
+#define WPS_CONFIG_METHOD_LABEL			0x0004
+#define WPS_CONFIG_METHOD_DISPLAY		0x0008
+#define WPS_CONFIG_METHOD_EXTERN_NFC	0x0010
+#define WPS_CONFIG_METHOD_INTERN_NFC	0x0020
+#define WPS_CONFIG_METHOD_NFC_IFACE		0x0040
+#define WPS_CONFIG_METHOD_PBC			0x0080
+#define WPS_CONFIG_METHOD_KEYPAD		0x0100
+
+#define WPS_CONFIGURATION_ERROR_NO_ERROR	0
+#define WPS_CONFIGURATION_ERROR_OOB_IF_READ	1
+#define WPS_CONFIGURATION_ERROR_DECRYPT_CRC	2
+#define WPS_CONFIGURATION_ERROR_24_NOT_SUP	3
+#define WPS_CONFIGURATION_ERROR_50_NOT_SUP	4
+#define WPS_CONFIGURATION_ERROR_SIG_WEAK	5
+#define WPS_CONFIGURATION_ERROR_NET_AUTH	6
+#define WPS_CONFIGURATION_ERROR_NET_ASSOC	7
+#define WPS_CONFIGURATION_ERROR_NO_DHCP		8
+#define WPS_CONFIGURATION_ERROR_DHCP_CONF	9
+#define WPS_CONFIGURATION_ERROR_IP_CONFLICT	10
+#define WPS_CONFIGURATION_ERROR_CONN_REG	11
+#define WPS_CONFIGURATION_ERROR_MULTI_PBC	12
+#define WPS_CONFIGURATION_ERROR_ROUGE_ACT	13
+#define WPS_CONFIGURATION_ERROR_DEV_BUSY	14
+#define WPS_CONFIGURATION_ERROR_SETUP_LOCK	15
+#define WPS_CONFIGURATION_ERROR_MSG_TIMEOUT	16
+#define WPS_CONFIGURATION_ERROR_REG_TIMEOUT	17
+#define WPS_CONFIGURATION_ERROR_DEV_PASSWD	18
+
+#define WPS_CONNECTION_TYPE_ESS			0x01
+#define WPS_CONNECTION_TYPE_IBSS		0x02
+
+#define WPS_DEVICE_PASSWORD_ID_PIN		0x0000
+#define WPS_DEVICE_PASSWORD_ID_USER		0x0001
+#define WPS_DEVICE_PASSWORD_ID_MACHINE	0x0002
+#define WPS_DEVICE_PASSWORD_ID_REKEY	0x0003
+#define WPS_DEVICE_PASSWORD_ID_PBC		0x0004
+#define WPS_DEVICE_PASSWORD_ID_REGISTRAR 0x0005
+
+#define WPS_ENCRYPTION_TYPE_NONE		0x0001
+#define WPS_ENCRYPTION_TYPE_WEP			0x0002
+#define WPS_ENCRYPTION_TYPE_TKIP		0x0004
+#define WPS_ENCRYPTION_TYPE_AES			0x0008
+
+#define WPS_MESSAGE_TYPE_M1				0x04
+#define WPS_MESSAGE_TYPE_M2				0x05
+#define WPS_MESSAGE_TYPE_M2D			0x06
+#define WPS_MESSAGE_TYPE_M3				0x07
+#define WPS_MESSAGE_TYPE_M4				0x08
+#define WPS_MESSAGE_TYPE_M5				0x09
+#define WPS_MESSAGE_TYPE_M6				0x0a
+#define WPS_MESSAGE_TYPE_M7				0x0b
+#define WPS_MESSAGE_TYPE_M8				0x0c
+#define WPS_MESSAGE_TYPE_WSC_ACK		0x0d
+#define WPS_MESSAGE_TYPE_WSC_NACK		0x0e
+#define WPS_MESSAGE_TYPE_WSC_Done		0x0f
+
+#define WPS_CATEGORY_COMPUTER			1
+#define WPS_CATEGORY_IO_DEVICE			2
+#define WPS_CATEGORY_PRINTER_SCANNER	3
+#define WPS_CATEGORY_CAMERA				4
+#define WPS_CATEGORY_STORAGE			5
+#define WPS_CATEGORY_NETWORK			6
+#define WPS_CATEGORY_DISPLAYS			7
+#define WPS_CATEGORY_MULTIMEDIA			8
+#define WPS_CATEGORY_GAMING				9
+#define WPS_CATEGORY_TELEPHONE			10
+
+#define WPS_REQUEST_TYPE_ENROLLEE_INFO	0x00
+#define WPS_REQUEST_TYPE_ENROLLEE_OPEN	0x01
+#define WPS_REQUEST_TYPE_REGISTRAR		0x02
+#define WPS_REQUEST_TYPE_WLAN_MANAGER	0x03
+
+#define WPS_RF_BAND_2_4GHZ				0x01
+#define WPS_RF_BAND_5_0GHZ				0x02
+
+#define WPS_SUBCATEGORY_PC				1
+#define WPS_SUBCATEGORY_SERVER			2
+#define WPS_SUBCATEGORY_MEDIA_CENTER	3
+#define WPS_SUBCATEGORY_PRINTER			1
+#define WPS_SUBCATEGORY_SCANNER			2
+#define WPS_SUBCATEGORY_STILL_CAMERA	1
+#define WPS_SUBCATEGORY_NAS				1
+#define WPS_SUBCATEGORY_AP				1
+#define WPS_SUBCATEGORY_ROUTER			2
+#define WPS_SUBCATEGORY_SWITCH			3
+#define WPS_SUBCATEGORY_TELEVISION		1
+#define WPS_SUBCATEGORY_PICTURE_FRAME	2
+#define WPS_SUBCATEGORY_PROJECTOR		3
+#define WPS_SUBCATEGORY_DAR				1
+#define WPS_SUBCATEGORY_PVR				2
+#define WPS_SUBCATEGORY_MCX				3
+#define WPS_SUBCATEGORY_XBOX			1
+#define WPS_SUBCATEGORY_XBOX360			2
+#define WPS_SUBCATEGORY_PLAYSTATION		3
+#define WPS_SUBCATEGORY_WINDOWS_MOBILE	1
+
+#define WPS_SETUP_STATE_NOT_CONFIGURED	0x01
+#define WPS_SETUP_STATE_CONFIGURED		0x02
+
+#define WPS_VERSION_1_0					0x10
+
+/**
+ * WPS_WALK_TIME - Amount of time allowed to start PBC configuration
+ * 					Time in seconds
+ */
+#define WPS_WALK_TIME					120
+
+
+/**
+ * WPS_PIN_PBC - PIN value for PBC
+ */
+#define WPS_PIN_PBC						"00000000"
+
+#endif /* WPS_H */
diff -urN wpa_supplicant-0.5.10.orig/wps_i.h wpa_supplicant-0.5.10/wps_i.h
--- wpa_supplicant-0.5.10.orig/wps_i.h	1969-12-31 16:00:00.000000000 -0800
+++ wpa_supplicant-0.5.10/wps_i.h	2008-08-20 14:38:10.000000000 -0700
@@ -0,0 +1,27 @@
+/*
+ * WiFi Protected Setup - WPS interface definitions
+ * Copyright (c) 2008, Chuck Tuffli <chuck@tuffli.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+int wpa_parse_wps_ie(const u8 *wps_ie, size_t wps_ie_len,
+		    struct wps_ie_data *data);
+int wpa_create_wps_ie(const char *uuid_str, u16 method, u8 *wps_ie,
+		size_t *wps_ie_len);
+int wps_config_pbc(struct wpa_supplicant *wpa_s);
+int wps_cancel_walktime_timeout(struct wpa_supplicant *wpa_s);
+int wps_get_pin(struct wpa_supplicant *wpa_s, char *buf, size_t buflen);
+int wps_config_pin(struct wpa_supplicant *wpa_s);
+
+#endif /* WPS_I_H */
