[pkg-wpa-devel] r1092 - in /wpasupplicant/branches/upstream/current/src/wps: ./ .gitignore Makefile wps.c wps.h wps_common.c wps_defs.h wps_dev_attr.c wps_dev_attr.h wps_enrollee.c wps_i.h wps_registrar.c

kelmo-guest at users.alioth.debian.org kelmo-guest at users.alioth.debian.org
Fri Feb 1 15:03:36 UTC 2008


Author: kelmo-guest
Date: Fri Feb  1 15:03:36 2008
New Revision: 1092

URL: http://svn.debian.org/wsvn/?sc=1&rev=1092
Log:
[svn-upgrade] Integrating new upstream version, wpasupplicant (0.6.2+git20080202.gde6ccd7)

Added:
    wpasupplicant/branches/upstream/current/src/wps/
    wpasupplicant/branches/upstream/current/src/wps/.gitignore
    wpasupplicant/branches/upstream/current/src/wps/Makefile
    wpasupplicant/branches/upstream/current/src/wps/wps.c
    wpasupplicant/branches/upstream/current/src/wps/wps.h
    wpasupplicant/branches/upstream/current/src/wps/wps_common.c
    wpasupplicant/branches/upstream/current/src/wps/wps_defs.h
    wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.c
    wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.h
    wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c
    wpasupplicant/branches/upstream/current/src/wps/wps_i.h
    wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c

Added: wpasupplicant/branches/upstream/current/src/wps/.gitignore
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/.gitignore?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/.gitignore (added)
+++ wpasupplicant/branches/upstream/current/src/wps/.gitignore Fri Feb  1 15:03:36 2008
@@ -1,0 +1,1 @@
+*.d

Added: wpasupplicant/branches/upstream/current/src/wps/Makefile
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/Makefile?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/Makefile (added)
+++ wpasupplicant/branches/upstream/current/src/wps/Makefile Fri Feb  1 15:03:36 2008
@@ -1,0 +1,6 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	for d in $(SUBDIRS); do make -C $$d clean; done
+	rm -f *~ *.o *.d

Added: wpasupplicant/branches/upstream/current/src/wps/wps.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps.c?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps.c Fri Feb  1 15:03:36 2008
@@ -1,0 +1,108 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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 "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+struct wps_data * wps_init(const struct wps_config *cfg)
+{
+	struct wps_data *data = os_zalloc(sizeof(*data));
+	if (data == NULL)
+		return NULL;
+	data->authenticator = cfg->authenticator;
+	data->wps = cfg->wps;
+	data->registrar = cfg->registrar;
+	if (cfg->enrollee_mac_addr)
+		os_memcpy(data->mac_addr_e, cfg->enrollee_mac_addr, ETH_ALEN);
+	if (cfg->uuid) {
+		os_memcpy(cfg->registrar ? data->uuid_r : data->uuid_e,
+			  cfg->uuid, WPS_UUID_LEN);
+	}
+	if (cfg->pin) {
+		data->dev_pw_id = DEV_PW_DEFAULT;
+		data->dev_password = os_malloc(cfg->pin_len);
+		if (data->dev_password == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
+		data->dev_password_len = cfg->pin_len;
+	}
+
+	data->pbc = cfg->pbc;
+	if (cfg->pbc) {
+		/* Use special PIN '00000000' for PBC */
+		data->dev_pw_id = DEV_PW_PUSHBUTTON;
+		os_free(data->dev_password);
+		data->dev_password = os_malloc(8);
+		if (data->dev_password == NULL) {
+			os_free(data);
+			return NULL;
+		}
+		os_memset(data->dev_password, '0', 8);
+		data->dev_password_len = 8;
+	}
+
+	data->wps_cred_cb = cfg->wps_cred_cb;
+	data->cb_ctx = cfg->cb_ctx;
+
+	data->state = data->registrar ? RECV_M1 : SEND_M1;
+
+	return data;
+}
+
+
+void wps_deinit(struct wps_data *data)
+{
+	if (data->wps_pin_revealed) {
+		wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
+			   "negotiation failed");
+		if (data->registrar)
+			wps_registrar_invalidate_pin(data->registrar,
+						     data->uuid_e);
+	} else if (data->registrar)
+		wps_registrar_unlock_pin(data->registrar, data->uuid_e);
+
+	wpabuf_free(data->dh_privkey);
+	wpabuf_free(data->dh_pubkey_e);
+	wpabuf_free(data->dh_pubkey_r);
+	wpabuf_free(data->last_msg);
+	os_free(data->dev_password);
+	os_free(data->new_psk);
+	wps_device_data_free(&data->peer_dev);
+	os_free(data);
+}
+
+
+enum wps_process_res wps_process_msg(struct wps_data *wps, u8 op_code,
+				     const struct wpabuf *msg)
+{
+	if (wps->registrar)
+		return wps_registrar_process_msg(wps, op_code, msg);
+	else
+		return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, u8 *op_code)
+{
+	if (wps->registrar)
+		return wps_registrar_get_msg(wps, op_code);
+	else
+		return wps_enrollee_get_msg(wps, op_code);
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps.h?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps.h Fri Feb  1 15:03:36 2008
@@ -1,0 +1,128 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007, Jouni Malinen <j at w1.fi>
+ *
+ * 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
+
+enum wsc_op_code {
+	WSC_Start = 0x01,
+	WSC_ACK = 0x02,
+	WSC_NACK = 0x03,
+	WSC_MSG = 0x04,
+	WSC_Done = 0x05,
+	WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+
+struct wps_credential {
+	u8 ssid[32];
+	size_t ssid_len;
+	u16 auth_type;
+	u16 encr_type;
+	u8 key_idx;
+	u8 key[64];
+	size_t key_len;
+	u8 mac_addr[ETH_ALEN];
+};
+
+struct wps_config {
+	int authenticator;
+	struct wps_context *wps;
+	struct wps_registrar *registrar; /* NULL for Enrollee */
+	const u8 *enrollee_mac_addr; /* NULL for Registrar */
+	const u8 *pin; /* Enrollee Device Password (NULL for Registrar or PBC)
+			*/
+	size_t pin_len;
+	const u8 *uuid; /* 128-bit Enrollee UUID (NULL for Registrar) */
+	int pbc;
+
+	int (*wps_cred_cb)(void *ctx, struct wps_credential *cred);
+	void *cb_ctx;
+};
+
+struct wps_data * wps_init(const struct wps_config *cfg);
+
+void wps_deinit(struct wps_data *data);
+
+enum wps_process_res {
+	WPS_DONE, WPS_CONTINUE, WPS_FAILURE, WPS_PENDING
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps, u8 op_code,
+				     const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, u8 *op_code);
+
+
+struct wps_device_data {
+	u8 mac_addr[ETH_ALEN];
+	char *device_name;
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+	u16 categ;
+	u32 oui;
+	u16 sub_categ;
+	u32 os_version;
+};
+
+
+struct wps_registrar_config {
+	int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+			  size_t psk_len);
+	int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len,
+			 const u8 *probe_resp_ie, size_t probe_resp_ie_len);
+	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+			      const struct wps_device_data *dev);
+	void *cb_ctx;
+};
+
+
+struct wps_context {
+	int ap;
+	struct wps_registrar *registrar;
+	int wps_state;
+	int ap_setup_locked;
+	u8 uuid[16];
+	u8 ssid[32];
+	size_t ssid_len;
+	struct wps_device_data dev;
+	u16 config_methods; /* bit field of WPS_CONFIG_* */
+	u16 encr_types; /* bit field of WPS_ENCR_* */
+	u16 auth_types; /* bit field of WPS_AUTH_* */
+	u8 *network_key; /* or NULL to generate per-device PSK */
+	size_t network_key_len;
+
+	int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+	void *cb_ctx;
+};
+
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+		   const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
+			  const u8 *pin, size_t pin_len);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_button_pushed(struct wps_registrar *reg);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+				const struct wpabuf *wps_data);
+
+struct wpabuf * wps_enrollee_build_assoc_req_ie(void);
+struct wpabuf * wps_enrollee_build_probe_req_ie(int pbc, const u8 *uuid);
+
+#endif /* WPS_H */

Added: wpasupplicant/branches/upstream/current/src/wps/wps_common.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_common.c?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_common.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_common.c Fri Feb  1 15:03:36 2008
@@ -1,0 +1,744 @@
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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 "dh_groups.h"
+#include "sha256.h"
+#include "aes_wrap.h"
+#include "crypto.h"
+#include "wps_i.h"
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+			const u8 *pos, u16 len)
+{
+	switch (type) {
+	case ATTR_VERSION:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
+				   len);
+			return -1;
+		}
+		attr->version = pos;
+		break;
+	case ATTR_MSG_TYPE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
+				   "length %u", len);
+			return -1;
+		}
+		attr->msg_type = pos;
+		break;
+	case ATTR_ENROLLEE_NONCE:
+		if (len != WPS_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
+				   "length %u", len);
+			return -1;
+		}
+		attr->enrollee_nonce = pos;
+		break;
+	case ATTR_REGISTRAR_NONCE:
+		if (len != WPS_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
+				   "length %u", len);
+			return -1;
+		}
+		attr->registrar_nonce = pos;
+		break;
+	case ATTR_UUID_E:
+		if (len != WPS_UUID_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
+				   len);
+			return -1;
+		}
+		attr->uuid_e = pos;
+		break;
+	case ATTR_UUID_R:
+		if (len != WPS_UUID_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
+				   len);
+			return -1;
+		}
+		attr->uuid_r = pos;
+		break;
+	case ATTR_AUTH_TYPE_FLAGS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+				   "Type Flags length %u", len);
+			return -1;
+		}
+		attr->auth_type_flags = pos;
+		break;
+	case ATTR_ENCR_TYPE_FLAGS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
+				   "Flags length %u", len);
+			return -1;
+		}
+		attr->encr_type_flags = pos;
+		break;
+	case ATTR_CONN_TYPE_FLAGS:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
+				   "Flags length %u", len);
+			return -1;
+		}
+		attr->conn_type_flags = pos;
+		break;
+	case ATTR_CONFIG_METHODS:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
+				   "length %u", len);
+			return -1;
+		}
+		attr->config_methods = pos;
+		break;
+	case ATTR_PRIMARY_DEV_TYPE:
+		if (len != sizeof(struct wps_dev_type)) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->primary_dev_type = pos;
+		break;
+	case ATTR_RF_BANDS:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
+				   "%u", len);
+			return -1;
+		}
+		attr->rf_bands = pos;
+		break;
+	case ATTR_ASSOC_STATE:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
+				   "length %u", len);
+			return -1;
+		}
+		attr->assoc_state = pos;
+		break;
+	case ATTR_CONFIG_ERROR:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
+				   "Error length %u", len);
+			return -1;
+		}
+		attr->config_error = pos;
+		break;
+	case ATTR_DEV_PASSWORD_ID:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
+				   "ID length %u", len);
+			return -1;
+		}
+		attr->dev_password_id = pos;
+		break;
+	case ATTR_OS_VERSION:
+		if (len != 4) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
+				   "%u", len);
+			return -1;
+		}
+		attr->os_version = pos;
+		break;
+	case ATTR_WPS_STATE:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
+				   "Setup State length %u", len);
+			return -1;
+		}
+		attr->wps_state = pos;
+		break;
+	case ATTR_AUTHENTICATOR:
+		if (len != WPS_AUTHENTICATOR_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
+				   "length %u", len);
+			return -1;
+		}
+		attr->authenticator = pos;
+		break;
+	case ATTR_R_HASH1:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
+				   len);
+			return -1;
+		}
+		attr->r_hash1 = pos;
+		break;
+	case ATTR_R_HASH2:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
+				   len);
+			return -1;
+		}
+		attr->r_hash2 = pos;
+		break;
+	case ATTR_E_HASH1:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
+				   len);
+			return -1;
+		}
+		attr->e_hash1 = pos;
+		break;
+	case ATTR_E_HASH2:
+		if (len != WPS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
+				   len);
+			return -1;
+		}
+		attr->e_hash2 = pos;
+		break;
+	case ATTR_R_SNONCE1:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->r_snonce1 = pos;
+		break;
+	case ATTR_R_SNONCE2:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->r_snonce2 = pos;
+		break;
+	case ATTR_E_SNONCE1:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->e_snonce1 = pos;
+		break;
+	case ATTR_E_SNONCE2:
+		if (len != WPS_SECRET_NONCE_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
+				   "%u", len);
+			return -1;
+		}
+		attr->e_snonce2 = pos;
+		break;
+	case ATTR_KEY_WRAP_AUTH:
+		if (len != WPS_KWA_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
+				   "Authenticator length %u", len);
+			return -1;
+		}
+		attr->key_wrap_auth = pos;
+		break;
+	case ATTR_AUTH_TYPE:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->auth_type = pos;
+		break;
+	case ATTR_ENCR_TYPE:
+		if (len != 2) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
+				   "Type length %u", len);
+			return -1;
+		}
+		attr->encr_type = pos;
+		break;
+	case ATTR_NETWORK_INDEX:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
+				   "length %u", len);
+			return -1;
+		}
+		attr->network_idx = pos;
+		break;
+	case ATTR_NETWORK_KEY_INDEX:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
+				   "length %u", len);
+			return -1;
+		}
+		attr->network_key_idx = pos;
+		break;
+	case ATTR_MAC_ADDR:
+		if (len != ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
+				   "length %u", len);
+			return -1;
+		}
+		attr->mac_addr = pos;
+		break;
+	case ATTR_KEY_PROVIDED_AUTO:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
+				   "Automatically length %u", len);
+			return -1;
+		}
+		attr->key_prov_auto = pos;
+		break;
+	case ATTR_802_1X_ENABLED:
+		if (len != 1) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
+				   "length %u", len);
+			return -1;
+		}
+		attr->dot1x_enabled = pos;
+		break;
+	case ATTR_MANUFACTURER:
+		attr->manufacturer = pos;
+		attr->manufacturer_len = len;
+		break;
+	case ATTR_MODEL_NAME:
+		attr->model_name = pos;
+		attr->model_name_len = len;
+		break;
+	case ATTR_MODEL_NUMBER:
+		attr->model_number = pos;
+		attr->model_number_len = len;
+		break;
+	case ATTR_SERIAL_NUMBER:
+		attr->serial_number = pos;
+		attr->serial_number_len = len;
+		break;
+	case ATTR_DEV_NAME:
+		attr->dev_name = pos;
+		attr->dev_name_len = len;
+		break;
+	case ATTR_PUBLIC_KEY:
+		attr->public_key = pos;
+		attr->public_key_len = len;
+		break;
+	case ATTR_ENCR_SETTINGS:
+		attr->encr_settings = pos;
+		attr->encr_settings_len = len;
+		break;
+	case ATTR_CRED:
+		if (attr->num_cred >= MAX_CRED_COUNT) {
+			wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
+				   "attribute (max %d credentials)",
+				   MAX_CRED_COUNT);
+			break;
+		}
+		attr->cred[attr->num_cred] = pos;
+		attr->cred_len[attr->num_cred] = len;
+		attr->num_cred++;
+		break;
+	case ATTR_SSID:
+		attr->ssid = pos;
+		attr->ssid_len = len;
+		break;
+	case ATTR_NETWORK_KEY:
+		attr->network_key = pos;
+		attr->network_key_len = len;
+		break;
+	case ATTR_EAP_TYPE:
+		attr->eap_type = pos;
+		attr->eap_type_len = len;
+		break;
+	case ATTR_EAP_IDENTITY:
+		attr->eap_identity = pos;
+		attr->eap_identity_len = len;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
+			   "len=%u", type, len);
+		break;
+	}
+
+	return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+	const u8 *pos, *end;
+	u16 type, len;
+
+	os_memset(attr, 0, sizeof(*attr));
+	pos = wpabuf_head(msg);
+	end = pos + wpabuf_len(msg);
+
+	while (pos < end) {
+		if (end - pos < 4) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
+				   "%lu bytes remaining",
+				   (unsigned long) (end - pos));
+			return -1;
+		}
+
+		type = WPA_GET_BE16(pos);
+		pos += 2;
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
+			   type, len);
+		if (len > end - pos) {
+			wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+			return -1;
+		}
+
+		if (wps_set_attr(attr, type, pos, len) < 0)
+			return -1;
+
+		pos += len;
+	}
+
+	return 0;
+}
+
+
+void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len)
+{
+	u8 i_buf[4], key_bits[4];
+	const u8 *addr[3];
+	size_t len[3];
+	int i, iter;
+	u8 hash[SHA256_MAC_LEN], *opos;
+	size_t left;
+
+	WPA_PUT_BE32(key_bits, res_len * 8);
+
+	addr[0] = i_buf;
+	len[0] = sizeof(i_buf);
+	addr[1] = (const u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = key_bits;
+	len[2] = sizeof(key_bits);
+
+	iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+	opos = res;
+	left = res_len;
+
+	for (i = 1; i <= iter; i++) {
+		WPA_PUT_BE32(i_buf, i);
+		hmac_sha256_vector(key, SHA256_MAC_LEN, 3, addr, len, hash);
+		if (i < iter) {
+			os_memcpy(opos, hash, SHA256_MAC_LEN);
+			opos += SHA256_MAC_LEN;
+			left -= SHA256_MAC_LEN;
+		} else
+			os_memcpy(opos, hash, left);
+	}
+	
+}
+
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
+{
+	struct wpabuf *pubkey;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
+	pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey);
+	if (pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
+			   "Diffie-Hellman handshake");
+		return -1;
+	}
+
+	wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+	wpabuf_put_be16(msg, wpabuf_len(pubkey));
+	wpabuf_put_buf(msg, pubkey);
+
+	if (wps->registrar) {
+		wpabuf_free(wps->dh_pubkey_r);
+		wps->dh_pubkey_r = pubkey;
+	} else {
+		wpabuf_free(wps->dh_pubkey_e);
+		wps->dh_pubkey_e = pubkey;
+	}
+
+	return 0;
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+	struct wpabuf *pubkey, *dh_shared;
+	u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+	const u8 *addr[3];
+	size_t len[3];
+	u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+	if (wps->dh_privkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
+		return -1;
+	}
+
+	pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+	if (pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
+		return -1;
+	}
+
+	dh_shared = dh_derive_shared(pubkey, wps->dh_privkey,
+				     dh_groups_get(WPS_DH_GROUP));
+	if (dh_shared == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
+		return -1;
+	}
+
+	/* Own DH private key is not needed anymore */
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = NULL;
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+	/* DHKey = SHA-256(g^AB mod p) */
+	addr[0] = wpabuf_head(dh_shared);
+	len[0] = wpabuf_len(dh_shared);
+	sha256_vector(1, addr, len, dhkey);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+	wpabuf_free(dh_shared);
+
+	/* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+	addr[0] = wps->nonce_e;
+	len[0] = WPS_NONCE_LEN;
+	addr[1] = wps->mac_addr_e;
+	len[1] = ETH_ALEN;
+	addr[2] = wps->nonce_r;
+	len[2] = WPS_NONCE_LEN;
+	hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+	wps_kdf(kdk, "Wi-Fi Easy and Secure Key Derivation",
+		keys, sizeof(keys));
+	os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+	os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+	os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+		  WPS_EMSK_LEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+			wps->authkey, WPS_AUTHKEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+			wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+	return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	if (wps->last_msg == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+			   "building authenticator");
+		return -1;
+	}
+
+	/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+	 * (M_curr* is M_curr without the Authenticator attribute)
+	 */
+	addr[0] = wpabuf_head(wps->last_msg);
+	len[0] = wpabuf_len(wps->last_msg);
+	addr[1] = wpabuf_head(msg);
+	len[1] = wpabuf_len(msg);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Authenticator");
+	wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+	wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+	wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+	return 0;
+}
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+			      const struct wpabuf *msg)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+
+	if (authenticator == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
+			   "included");
+		return -1;
+	}
+
+	if (wps->last_msg == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+			   "validating authenticator");
+		return -1;
+	}
+
+	/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+	 * (M_curr* is M_curr without the Authenticator attribute)
+	 */
+	addr[0] = wpabuf_head(wps->last_msg);
+	len[0] = wpabuf_len(wps->last_msg);
+	addr[1] = wpabuf_head(msg);
+	len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+	if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+		    size_t dev_passwd_len)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+		    (dev_passwd_len + 1) / 2, hash);
+	os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+		    dev_passwd + (dev_passwd_len + 1) / 2,
+		    dev_passwd_len / 2, hash);
+	os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+			      dev_passwd, dev_passwd_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+					  size_t encr_len)
+{
+	struct wpabuf *decrypted;
+	const size_t block_size = 16;
+	size_t i;
+	u8 pad;
+	const u8 *pos;
+
+	/* AES-128-CBC */
+	if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+	{
+		wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
+		return NULL;
+	}
+
+	decrypted = wpabuf_alloc(encr_len - block_size);
+	if (decrypted == NULL)
+		return NULL;
+
+	wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+	wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+	if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+				wpabuf_len(decrypted))) {
+		wpabuf_free(decrypted);
+		return NULL;
+	}
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+			    decrypted);
+
+	pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+	pad = *pos;
+	if (pad > wpabuf_len(decrypted)) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
+		wpabuf_free(decrypted);
+		return NULL;
+	}
+	for (i = 0; i < pad; i++) {
+		if (*pos-- != pad) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
+				   "string");
+			wpabuf_free(decrypted);
+			return NULL;
+		}
+	}
+	decrypted->used -= pad;
+
+	return decrypted;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+			      const u8 *key_wrap_auth)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *head;
+	size_t len;
+
+	if (key_wrap_auth == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
+		return -1;
+	}
+
+	head = wpabuf_head(msg);
+	len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+	if (head + len != key_wrap_auth - 4) {
+		wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
+			   "decrypted attribute");
+		return -1;
+	}
+
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+	if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 hash[SHA256_MAC_LEN];
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Key Wrap Authenticator");
+	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+		    wpabuf_len(msg), hash);
+
+	wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+	wpabuf_put_be16(msg, WPS_KWA_LEN);
+	wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+	return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+			    struct wpabuf *plain)
+{
+	size_t pad_len;
+	const size_t block_size = 16;
+	u8 *iv, *data;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Encrypted Settings");
+
+	/* PKCS#5 v2.0 pad */
+	pad_len = block_size - wpabuf_len(plain) % block_size;
+	os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+	wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+	wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+	iv = wpabuf_put(msg, block_size);
+	if (os_get_random(iv, block_size) < 0)
+		return -1;
+
+	data = wpabuf_put(msg, 0);
+	wpabuf_put_buf(msg, plain);
+	if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+		return -1;
+
+	return 0;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_defs.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_defs.h?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_defs.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_defs.h Fri Feb  1 15:03:36 2008
@@ -1,0 +1,294 @@
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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_DEFS_H
+#define WPS_DEFS_H
+
+#define WPS_VERSION 0x10
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+
+/* Attribute Types */
+enum wps_attribute {
+	ATTR_AP_CHANNEL = 0x1001,
+	ATTR_ASSOC_STATE = 0x1002,
+	ATTR_AUTH_TYPE = 0x1003,
+	ATTR_AUTH_TYPE_FLAGS = 0x1004,
+	ATTR_AUTHENTICATOR = 0x1005,
+	ATTR_CONFIG_METHODS = 0x1008,
+	ATTR_CONFIG_ERROR = 0x1009,
+	ATTR_CONFIRM_URL4 = 0x100a,
+	ATTR_CONFIRM_URL6 = 0x100b,
+	ATTR_CONN_TYPE = 0x100c,
+	ATTR_CONN_TYPE_FLAGS = 0x100d,
+	ATTR_CRED = 0x100e,
+	ATTR_ENCR_TYPE = 0x100f,
+	ATTR_ENCR_TYPE_FLAGS = 0x1010,
+	ATTR_DEV_NAME = 0x1011,
+	ATTR_DEV_PASSWORD_ID = 0x1012,
+	ATTR_E_HASH1 = 0x1014,
+	ATTR_E_HASH2 = 0x1015,
+	ATTR_E_SNONCE1 = 0x1016,
+	ATTR_E_SNONCE2 = 0x1017,
+	ATTR_ENCR_SETTINGS = 0x1018,
+	ATTR_ENROLLEE_NONCE = 0x101a,
+	ATTR_FEATURE_ID = 0x101b,
+	ATTR_IDENTITY = 0x101c,
+	ATTR_IDENTITY_PROOF = 0x101d,
+	ATTR_KEY_WRAP_AUTH = 0x101e,
+	ATTR_KEY_ID = 0x101f,
+	ATTR_MAC_ADDR = 0x1020,
+	ATTR_MANUFACTURER = 0x1021,
+	ATTR_MSG_TYPE = 0x1022,
+	ATTR_MODEL_NAME = 0x1023,
+	ATTR_MODEL_NUMBER = 0x1024,
+	ATTR_NETWORK_INDEX = 0x1026,
+	ATTR_NETWORK_KEY = 0x1027,
+	ATTR_NETWORK_KEY_INDEX = 0x1028,
+	ATTR_NEW_DEVICE_NAME = 0x1029,
+	ATTR_NEW_PASSWORD = 0x102a,
+	ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+	ATTR_OS_VERSION = 0x102d,
+	ATTR_POWER_LEVEL = 0x102f,
+	ATTR_PSK_CURRENT = 0x1030,
+	ATTR_PSK_MAX = 0x1031,
+	ATTR_PUBLIC_KEY = 0x1032,
+	ATTR_RADIO_ENABLE = 0x1033,
+	ATTR_REBOOT = 0x1034,
+	ATTR_REGISTRAR_CURRENT = 0x1035,
+	ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+	ATTR_REGISTRAR_LIST = 0x1037,
+	ATTR_REGISTRAR_MAX = 0x1038,
+	ATTR_REGISTRAR_NONCE = 0x1039,
+	ATTR_REQUEST_TYPE = 0x103a,
+	ATTR_RESPONSE_TYPE = 0x103b,
+	ATTR_RF_BANDS = 0x103c,
+	ATTR_R_HASH1 = 0x103d,
+	ATTR_R_HASH2 = 0x103e,
+	ATTR_R_SNONCE1 = 0x103f,
+	ATTR_R_SNONCE2 = 0x1040,
+	ATTR_SELECTED_REGISTRAR = 0x1041,
+	ATTR_SERIAL_NUMBER = 0x1042,
+	ATTR_WPS_STATE = 0x1044,
+	ATTR_SSID = 0x1045,
+	ATTR_TOTAL_NETWORKS = 0x1046,
+	ATTR_UUID_E = 0x1047,
+	ATTR_UUID_R = 0x1048,
+	ATTR_VENDOR_EXT = 0x1049,
+	ATTR_VERSION = 0x104a,
+	ATTR_X509_CERT_REQ = 0x104b,
+	ATTR_X509_CERT = 0x104c,
+	ATTR_EAP_IDENTITY = 0x104d,
+	ATTR_MSG_COUNTER = 0x104e,
+	ATTR_PUBKEY_HASH = 0x104f,
+	ATTR_REKEY_KEY = 0x1050,
+	ATTR_KEY_LIFETIME = 0x1051,
+	ATTR_PERMITTED_CFG_METHODS = 0x1052,
+	ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+	ATTR_PRIMARY_DEV_TYPE = 0x1054,
+	ATTR_SECONDARY_DEV_TYP_ELIST = 0x1055,
+	ATTR_PORTABLE_DEV = 0x1056,
+	ATTR_AP_SETUP_LOCKED = 0x1057,
+	ATTR_APPLICATION_EXT = 0x1058,
+	ATTR_EAP_TYPE = 0x1059,
+	ATTR_IV = 0x1060,
+	ATTR_KEY_PROVIDED_AUTO = 0x1061,
+	ATTR_802_1X_ENABLED = 0x1062,
+	ATTR_APPSESSIONKEY = 0x1063,
+	ATTR_WEPTRANSMITKEY = 0x1064
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+	DEV_PW_DEFAULT = 0x0000,
+	DEV_PW_USER_SPECIFIED = 0x0001,
+	DEV_PW_MACHINE_SPECIFIED = 0x0002,
+	DEV_PW_REKEY = 0x0003,
+	DEV_PW_PUSHBUTTON = 0x0004,
+	DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+};
+
+/* Message Type */
+enum wps_msg_type {
+	WPS_Beacon = 0x01,
+	WPS_ProbeRequest = 0x02,
+	WPS_ProbeResponse = 0x03,
+	WPS_M1 = 0x04,
+	WPS_M2 = 0x05,
+	WPS_M2D = 0x06,
+	WPS_M3 = 0x07,
+	WPS_M4 = 0x08,
+	WPS_M5 = 0x09,
+	WPS_M6 = 0x0a,
+	WPS_M7 = 0x0b,
+	WPS_M8 = 0x0c,
+	WPS_WSC_ACK = 0x0d,
+	WPS_WSC_NACK = 0x0e,
+	WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+			WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_TKIP 0x0004
+#define WPS_ENCR_AES 0x0008
+#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
+			WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+	WPS_CFG_NO_ERROR = 0,
+	WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+	WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+	WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+	WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+	WPS_CFG_SIGNAL_TOO_WEAK = 5,
+	WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+	WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+	WPS_CFG_NO_DHCP_RESPONSE = 8,
+	WPS_CFG_FAILED_DHCP_CONFIG = 9,
+	WPS_CFG_IP_ADDR_CONFLICT = 10,
+	WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+	WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+	WPS_CFG_ROGUE_SUSPECTED = 13,
+	WPS_CFG_DEVICE_BUSY = 14,
+	WPS_CFG_SETUP_LOCKED = 15,
+	WPS_CFG_MSG_TIMEOUT = 16,
+	WPS_CFG_REG_SESS_TIMEOUT = 17,
+	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+	WPS_STATE_NOT_CONFIGURED = 1,
+	WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+	WPS_ASSOC_NOT_ASSOC = 0,
+	WPS_ASSOC_CONN_SUCCESS = 1,
+	WPS_ASSOC_CFG_FAILURE = 2,
+	WPS_ASSOC_FAILURE = 3,
+	WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+/* Primary Device Type */
+struct wps_dev_type {
+	u8 categ_id[2];
+	u8 oui[4];
+	u8 sub_categ_id[2];
+};
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+	WPS_DEV_COMPUTER = 1,
+	WPS_DEV_INPUT = 2,
+	WPS_DEV_PRINTER = 3,
+	WPS_DEV_CAMERA = 4,
+	WPS_DEV_STORAGE = 5,
+	WPS_DEV_NETWORK_INFRA = 6,
+	WPS_DEV_DISPLAY = 7,
+	WPS_DEV_MULTIMEDIA = 8,
+	WPS_DEV_GAMING = 9,
+	WPS_DEV_PHONE = 10
+};
+
+enum wps_dev_subcateg {
+	WPS_DEV_COMPUTER_PC = 1,
+	WPS_DEV_COMPUTER_SERVER = 2,
+	WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+	WPS_DEV_PRINTER_PRINTER = 1,
+	WPS_DEV_PRINTER_SCANNER = 2,
+	WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+	WPS_DEV_STORAGE_NAS = 1,
+	WPS_DEV_NETWORK_INFRA_AP = 1,
+	WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+	WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+	WPS_DEV_DISPLAY_TV = 1,
+	WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+	WPS_DEV_DISPLAY_PROJECTOR = 3,
+	WPS_DEV_MULTIMEDIA_DAR = 1,
+	WPS_DEV_MULTIMEDIA_PVR = 2,
+	WPS_DEV_MULTIMEDIA_MCX = 3,
+	WPS_DEV_GAMING_XBOX = 1,
+	WPS_DEV_GAMING_XBOX360 = 2,
+	WPS_DEV_GAMING_PLAYSTATION = 3,
+	WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+};
+
+
+/* Request Type */
+enum wps_request_type {
+	WPS_REQ_ENROLLEE_INFO = 0,
+	WPS_REQ_ENROLLEE = 1,
+	WPS_REQ_REGISTRAR = 2,
+	WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+	WPS_RESP_ENROLLEE_INFO = 0,
+	WPS_RESP_ENROLLEE = 1,
+	WPS_RESP_REGISTRAR = 2,
+	WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#endif /* WPS_DEFS_H */

Added: wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.c?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.c Fri Feb  1 15:03:36 2008
@@ -1,0 +1,316 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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 "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+static int wps_build_manufacturer(struct wps_device_data *dev,
+				  struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Manufacturer");
+	wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+	len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->manufacturer, len);
+	return 0;
+}
+
+
+static int wps_build_model_name(struct wps_device_data *dev,
+				struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Model Name");
+	wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+	len = dev->model_name ? os_strlen(dev->model_name) : 0;
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->model_name, len);
+	return 0;
+}
+
+
+static int wps_build_model_number(struct wps_device_data *dev,
+				  struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Model Number");
+	wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+	len = dev->model_number ? os_strlen(dev->model_number) : 0;
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->model_number, len);
+	return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_device_data *dev,
+				   struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
+	wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+	len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->serial_number, len);
+	return 0;
+}
+
+
+static int wps_build_primary_dev_type(struct wps_device_data *dev,
+				      struct wpabuf *msg)
+{
+	struct wps_dev_type *d;
+	wpa_printf(MSG_DEBUG, "WPS:  * Primary Device Type");
+	wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+	wpabuf_put_be16(msg, sizeof(*d));
+	d = wpabuf_put(msg, sizeof(*d));
+	WPA_PUT_BE16(d->categ_id, dev->categ);
+	WPA_PUT_BE32(d->oui, dev->oui);
+	WPA_PUT_BE16(d->sub_categ_id, dev->sub_categ);
+	return 0;
+}
+
+
+static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	size_t len;
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Name");
+	wpabuf_put_be16(msg, ATTR_DEV_NAME);
+	len = dev->device_name ? os_strlen(dev->device_name) : 0;
+	wpabuf_put_be16(msg, len);
+	wpabuf_put_data(msg, dev->device_name, len);
+	return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	if (wps_build_manufacturer(dev, msg) ||
+	    wps_build_model_name(dev, msg) ||
+	    wps_build_model_number(dev, msg) ||
+	    wps_build_serial_number(dev, msg) ||
+	    wps_build_primary_dev_type(dev, msg) ||
+	    wps_build_dev_name(dev, msg))
+		return -1;
+	return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * OS Version");
+	wpabuf_put_be16(msg, ATTR_OS_VERSION);
+	wpabuf_put_be16(msg, 4);
+	wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+	return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+				    size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+	os_free(dev->manufacturer);
+	dev->manufacturer = os_malloc(str_len + 1);
+	if (dev->manufacturer == NULL)
+		return -1;
+	os_memcpy(dev->manufacturer, str, str_len);
+	dev->manufacturer[str_len] = '\0';
+
+	return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+				  size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+	os_free(dev->model_name);
+	dev->model_name = os_malloc(str_len + 1);
+	if (dev->model_name == NULL)
+		return -1;
+	os_memcpy(dev->model_name, str, str_len);
+	dev->model_name[str_len] = '\0';
+
+	return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+				    size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+	os_free(dev->model_number);
+	dev->model_number = os_malloc(str_len + 1);
+	if (dev->model_number == NULL)
+		return -1;
+	os_memcpy(dev->model_number, str, str_len);
+	dev->model_number[str_len] = '\0';
+
+	return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+				     const u8 *str, size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+	os_free(dev->serial_number);
+	dev->serial_number = os_malloc(str_len + 1);
+	if (dev->serial_number == NULL)
+		return -1;
+	os_memcpy(dev->serial_number, str, str_len);
+	dev->serial_number[str_len] = '\0';
+
+	return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+				size_t str_len)
+{
+	if (str == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
+		return -1;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+	os_free(dev->device_name);
+	dev->device_name = os_malloc(str_len + 1);
+	if (dev->device_name == NULL)
+		return -1;
+	os_memcpy(dev->device_name, str, str_len);
+	dev->device_name[str_len] = '\0';
+
+	return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+					const u8 *dev_type)
+{
+	struct wps_dev_type *d;
+
+	if (dev_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
+		return -1;
+	}
+
+	d = (struct wps_dev_type *) dev_type;
+	dev->categ = WPA_GET_BE16(d->categ_id);
+	dev->oui = WPA_GET_BE32(d->oui);
+	dev->sub_categ = WPA_GET_BE16(d->sub_categ_id);
+
+	wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: category %d "
+		   "OUI %08x sub-category %d",
+		   dev->categ, dev->oui, dev->sub_categ);
+
+	return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+			     struct wps_parse_attr *attr)
+{
+	if (wps_process_manufacturer(dev, attr->manufacturer,
+				     attr->manufacturer_len) ||
+	    wps_process_model_name(dev, attr->model_name,
+				   attr->model_name_len) ||
+	    wps_process_model_number(dev, attr->model_number,
+				     attr->model_number_len) ||
+	    wps_process_serial_number(dev, attr->serial_number,
+				      attr->serial_number_len) ||
+	    wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+	    wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+		return -1;
+	return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+	if (ver == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
+		return -1;
+	}
+
+	dev->os_version = WPA_GET_BE32(ver);
+	wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
+
+	return 0;
+}
+
+
+void wps_device_data_dup(struct wps_device_data *dst,
+			 const struct wps_device_data *src)
+{
+	if (src->device_name)
+		dst->device_name = os_strdup(src->device_name);
+	if (src->manufacturer)
+		dst->manufacturer = os_strdup(src->manufacturer);
+	if (src->model_name)
+		dst->model_name = os_strdup(src->model_name);
+	if (src->model_number)
+		dst->model_number = os_strdup(src->model_number);
+	if (src->serial_number)
+		dst->serial_number = os_strdup(src->serial_number);
+	dst->categ = src->categ;
+	dst->oui = src->oui;
+	dst->sub_categ = src->sub_categ;
+	dst->os_version = src->os_version;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+	os_free(dev->device_name);
+	dev->device_name = NULL;
+	os_free(dev->manufacturer);
+	dev->manufacturer = NULL;
+	os_free(dev->model_name);
+	dev->model_name = NULL;
+	os_free(dev->model_number);
+	dev->model_number = NULL;
+	os_free(dev->serial_number);
+	dev->serial_number = NULL;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.h?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_dev_attr.h Fri Feb  1 15:03:36 2008
@@ -1,0 +1,29 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+			     struct wps_parse_attr *attr);
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+void wps_device_data_dup(struct wps_device_data *dst,
+			 const struct wps_device_data *src);
+void wps_device_data_free(struct wps_device_data *dev);
+
+#endif /* WPS_DEV_ATTR_H */

Added: wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_enrollee.c Fri Feb  1 15:03:36 2008
@@ -1,0 +1,1534 @@
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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 "sha256.h"
+#include "ieee802_11_defs.h"
+#include "wps_i.h"
+
+
+static int wps_build_version(struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Version");
+	wpabuf_put_be16(msg, ATTR_VERSION);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_VERSION);
+	return 0;
+}
+
+
+static int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Request Type");
+	wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, type);
+	return 0;
+}
+
+
+static int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Message Type (%d)", msg_type);
+	wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, msg_type);
+	return 0;
+}
+
+
+static int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * UUID-E");
+	wpabuf_put_be16(msg, ATTR_UUID_E);
+	wpabuf_put_be16(msg, WPS_UUID_LEN);
+	wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+	return 0;
+}
+
+
+static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address");
+	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+	wpabuf_put_be16(msg, ETH_ALEN);
+	wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+	return 0;
+}
+
+
+static int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Enrollee Nonce");
+	wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Registrar Nonce");
+	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+	return 0;
+}
+
+
+static int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+	return 0;
+}
+
+
+static int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Connection Type Flags");
+	wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_CONN_ESS);
+	return 0;
+}
+
+
+static int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods");
+	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State");
+	wpabuf_put_be16(msg, ATTR_WPS_STATE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_STATE_CONFIGURED);
+	return 0;
+}
+
+
+static int wps_build_manufacturer(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Manufacturer");
+	wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+	wpabuf_put_be16(msg, 5);
+	wpabuf_put_data(msg, "manuf", 5); /* FIX */
+	return 0;
+}
+
+
+static int wps_build_model_name(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Model Name");
+	wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+	wpabuf_put_be16(msg, 10);
+	wpabuf_put_data(msg, "model name", 10); /* FIX */
+	return 0;
+}
+
+
+static int wps_build_model_number(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Model Number");
+	wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+	wpabuf_put_be16(msg, 12);
+	wpabuf_put_data(msg, "model number", 12); /* FIX */
+	return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
+	wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+	wpabuf_put_be16(msg, 5);
+	wpabuf_put_data(msg, "12345", 5); /* FIX */
+	return 0;
+}
+
+
+static int wps_build_primary_dev_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	struct wps_dev_type *dev;
+	wpa_printf(MSG_DEBUG, "WPS:  * Primary Device Type");
+	wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+	wpabuf_put_be16(msg, sizeof(*dev));
+	dev = wpabuf_put(msg, sizeof(*dev));
+	WPA_PUT_BE16(dev->categ_id, WPS_DEV_COMPUTER);
+	WPA_PUT_BE32(dev->oui, WPS_DEV_OUI_WFA);
+	WPA_PUT_BE16(dev->sub_categ_id, WPS_DEV_COMPUTER_PC);
+	return 0;
+}
+
+
+static int wps_build_dev_name(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Name");
+	wpabuf_put_be16(msg, ATTR_DEV_NAME);
+	wpabuf_put_be16(msg, 8);
+	wpabuf_put_data(msg, "dev name", 8); /* FIX */
+	return 0;
+}
+
+
+static int wps_build_rf_bands(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands");
+	wpabuf_put_be16(msg, ATTR_RF_BANDS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_RF_24GHZ | WPS_RF_50GHZ);
+	return 0;
+}
+
+
+static int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Association State");
+	wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+	return 0;
+}
+
+
+static int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID");
+	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, id);
+	return 0;
+}
+
+
+static int wps_build_config_error(struct wps_data *wps, struct wpabuf *msg)
+{
+	u16 err = WPS_CFG_NO_ERROR;
+	wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+	wpabuf_put_be16(msg, 2);
+	if (wps && wps->authenticator && wps->wps->ap_setup_locked)
+		err = WPS_CFG_SETUP_LOCKED;
+	wpa_printf(MSG_DEBUG, "WPS:  * Configuration Error (%d)", err);
+	wpabuf_put_be16(msg, err);
+	return 0;
+}
+
+
+static int wps_build_os_version(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * OS Version");
+	wpabuf_put_be16(msg, ATTR_OS_VERSION);
+	wpabuf_put_be16(msg, 4);
+	wpabuf_put_be32(msg, 0x80000000); /* FIX */
+	return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 *hash;
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+			   "E-Hash derivation");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
+	wpabuf_put_be16(msg, ATTR_E_HASH1);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = wps->snonce;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
+	wpabuf_put_be16(msg, ATTR_E_HASH2);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
+	wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
+	wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+			WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+	u16 methods;
+
+	if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0)
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+		    wps->nonce_e, WPS_NONCE_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+	if (wps->pbc)
+		methods |= WPS_CONFIG_PUSHBUTTON;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M1) ||
+	    wps_build_uuid_e(msg, wps->uuid_e) ||
+	    wps_build_mac_addr(wps, msg) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_public_key(wps, msg) ||
+	    wps_build_auth_type_flags(wps, msg) ||
+	    wps_build_encr_type_flags(wps, msg) ||
+	    wps_build_conn_type_flags(wps, msg) ||
+	    wps_build_config_methods(msg, methods) ||
+	    wps_build_wps_state(wps, msg) ||
+	    wps_build_manufacturer(wps, msg) ||
+	    wps_build_model_name(wps, msg) ||
+	    wps_build_model_number(wps, msg) ||
+	    wps_build_serial_number(wps, msg) ||
+	    wps_build_primary_dev_type(wps, msg) ||
+	    wps_build_dev_name(wps, msg) ||
+	    wps_build_rf_bands(wps, msg) ||
+	    wps_build_assoc_state(wps, msg) ||
+	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+	    wps_build_config_error(wps, msg) ||
+	    wps_build_os_version(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M2;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
+
+	if (wps->dev_password == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
+		return NULL;
+	}
+	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M3) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_e_hash(wps, msg) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M4;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M5) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_e_snonce1(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_M6;
+	return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, wps->wps->ssid_len);
+	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type");
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, wps->wps->auth_types);
+	return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type");
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, wps->wps->encr_types);
+	return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
+	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+	wpabuf_put_be16(msg, wps->wps->network_key_len);
+	wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
+	return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
+	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+	wpabuf_put_be16(msg, ETH_ALEN);
+	wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+	return 0;
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
+
+	plain = wpabuf_alloc(500);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M7) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_e_snonce2(wps, plain) ||
+	    (wps->authenticator &&
+	     (wps_build_cred_ssid(wps, plain) ||
+	      wps_build_cred_mac_addr(wps, plain) ||
+	      wps_build_cred_auth_type(wps, plain) ||
+	      wps_build_cred_encr_type(wps, plain) ||
+	      wps_build_cred_network_key(wps, plain))) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_M8;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_DONE) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = wps->authenticator ? RECV_ACK : WPS_FINISHED;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_ACK) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_WSC_NACK) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_config_error(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, u8 *op_code)
+{
+	struct wpabuf *msg;
+
+	switch (wps->state) {
+	case SEND_M1:
+		msg = wps_build_m1(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M3:
+		msg = wps_build_m3(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M5:
+		msg = wps_build_m5(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M7:
+		msg = wps_build_m7(wps);
+		*op_code = WSC_MSG;
+		break;
+	case RECEIVED_M2D:
+		msg = wps_build_wsc_ack(wps);
+		*op_code = WSC_ACK;
+		if (msg) {
+			/* Another M2/M2D may be received */
+			wps->state = RECV_M2;
+		}
+		break;
+	case SEND_WSC_NACK:
+		msg = wps_build_wsc_nack(wps);
+		*op_code = WSC_NACK;
+		break;
+	case WPS_MSG_DONE:
+		msg = wps_build_wsc_done(wps);
+		*op_code = WSC_Done;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+			   "a message", wps->state);
+		msg = NULL;
+		break;
+	}
+
+	if (*op_code == WSC_MSG && msg) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+	if (r_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+		return -1;
+	}
+
+	os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+		    wps->nonce_r, WPS_NONCE_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+	if (e_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+		return -1;
+	}
+
+	if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+	if (uuid_r == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
+		return -1;
+	}
+
+	os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+			      size_t pk_len)
+{
+	if (pk == NULL || pk_len == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+		return -1;
+	}
+
+	wpabuf_free(wps->dh_pubkey_r);
+	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
+	if (wps->dh_pubkey_r == NULL)
+		return -1;
+
+	return wps_derive_keys(wps);
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+	if (r_hash1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+	if (r_hash2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (r_snonce1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+			WPS_SECRET_NONCE_LEN);
+
+	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = r_snonce1;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
+			   "not match with the pre-committed value");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
+		   "half of the device password");
+
+	return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (r_snonce2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+			WPS_SECRET_NONCE_LEN);
+
+	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = r_snonce2;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
+			   "not match with the pre-committed value");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
+		   "half of the device password");
+
+	return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+					const u8 *idx)
+{
+	if (idx == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Network Index");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+
+	return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+				 size_t ssid_len)
+{
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+		return -1;
+	}
+
+	/* Remove zero-padding since some Registrar implementations seem to use
+	 * hardcoded 32-octet length for this attribute */
+	while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+		ssid_len--;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+	if (ssid_len <= sizeof(cred->ssid)) {
+		os_memcpy(cred->ssid, ssid, ssid_len);
+		cred->ssid_len = ssid_len;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+				      const u8 *auth_type)
+{
+	if (auth_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Authentication Type");
+		return -1;
+	}
+
+	cred->auth_type = WPA_GET_BE16(auth_type);
+	wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
+		   cred->auth_type);
+
+	return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+				      const u8 *encr_type)
+{
+	if (encr_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Encryption Type");
+		return -1;
+	}
+
+	cred->encr_type = WPA_GET_BE16(encr_type);
+	wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
+		   cred->encr_type);
+
+	return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+					    const u8 *key_idx)
+{
+	if (key_idx == NULL)
+		return 0; /* optional attribute */
+
+	wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
+	cred->key_idx = *key_idx;
+
+	return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+					const u8 *key, size_t key_len)
+{
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "Network Key");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+	if (key_len <= sizeof(cred->key)) {
+		os_memcpy(cred->key, key, key_len);
+		cred->key_len = key_len;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+				     const u8 *mac_addr)
+{
+	if (mac_addr == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+			   "MAC Address");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+	os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int wps_process_cred_eap_type(struct wps_credential *cred,
+				     const u8 *eap_type, size_t eap_type_len)
+{
+	if (eap_type == NULL)
+		return 0; /* optional attribute */
+
+	wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
+
+	return 0;
+}
+
+
+static int wps_process_cred_eap_identity(struct wps_credential *cred,
+					 const u8 *identity,
+					 size_t identity_len)
+{
+	if (identity == NULL)
+		return 0; /* optional attribute */
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
+			  identity, identity_len);
+
+	return 0;
+}
+
+
+static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
+					  const u8 *key_prov_auto)
+{
+	if (key_prov_auto == NULL)
+		return 0; /* optional attribute */
+
+	wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
+		   *key_prov_auto);
+
+	return 0;
+}
+
+
+static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
+					   const u8 *dot1x_enabled)
+{
+	if (dot1x_enabled == NULL)
+		return 0; /* optional attribute */
+
+	wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
+
+	return 0;
+}
+
+
+static int wps_process_cred(struct wps_data *wps, const u8 *cred,
+			    size_t cred_len)
+{
+	struct wps_parse_attr attr;
+	struct wpabuf msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received Credential");
+	os_memset(&wps->cred, 0, sizeof(wps->cred));
+	wpabuf_set(&msg, cred, cred_len);
+	/* TODO: support multiple Network Keys */
+	if (wps_parse_msg(&msg, &attr) < 0 ||
+	    wps_process_cred_network_idx(&wps->cred, attr.network_idx) ||
+	    wps_process_cred_ssid(&wps->cred, attr.ssid, attr.ssid_len) ||
+	    wps_process_cred_auth_type(&wps->cred, attr.auth_type) ||
+	    wps_process_cred_encr_type(&wps->cred, attr.encr_type) ||
+	    wps_process_cred_network_key_idx(&wps->cred, attr.network_key_idx)
+	    ||
+	    wps_process_cred_network_key(&wps->cred, attr.network_key,
+					 attr.network_key_len) ||
+	    wps_process_cred_mac_addr(&wps->cred, attr.mac_addr) ||
+	    wps_process_cred_eap_type(&wps->cred, attr.eap_type,
+				      attr.eap_type_len) ||
+	    wps_process_cred_eap_identity(&wps->cred, attr.eap_identity,
+					  attr.eap_identity_len) ||
+	    wps_process_cred_key_prov_auto(&wps->cred, attr.key_prov_auto) ||
+	    wps_process_cred_802_1x_enabled(&wps->cred, attr.dot1x_enabled))
+		return -1;
+
+	if (wps->wps_cred_cb)
+		wps->wps_cred_cb(wps->cb_ctx, &wps->cred);
+
+	return 0;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+			     size_t cred_len[], size_t num_cred)
+{
+	size_t i;
+
+	if (wps->authenticator)
+		return 0;
+
+	if (num_cred == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
+			   "received");
+		return -1;
+	}
+
+	for (i = 0; i < num_cred; i++) {
+		if (wps_process_cred(wps, cred[i], cred_len[i]))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_ap_settings(struct wps_data *wps,
+				   struct wps_parse_attr *attr)
+{
+	struct wps_credential cred;
+
+	if (!wps->authenticator)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
+	os_memset(&cred, 0, sizeof(cred));
+	/* TODO: optional attributes New Password and Device Password ID */
+	if (wps_process_cred_ssid(&cred, attr->ssid, attr->ssid_len) ||
+	    wps_process_cred_auth_type(&cred, attr->auth_type) ||
+	    wps_process_cred_encr_type(&cred, attr->encr_type) ||
+	    wps_process_cred_network_key_idx(&cred, attr->network_key_idx) ||
+	    wps_process_cred_network_key(&cred, attr->network_key,
+					 attr->network_key_len) ||
+	    wps_process_cred_mac_addr(&cred, attr->mac_addr))
+		return -1;
+
+	wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+		   "Registrar");
+
+	if (wps->wps->cred_cb)
+		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+	return 0;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M2");
+
+	if (wps->state != RECV_M2) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M2", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_uuid_r(wps, attr->uuid_r) ||
+	    wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg))
+		return WPS_FAILURE;
+
+	if (wps->authenticator && wps->wps->ap_setup_locked) {
+		wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+			   "registration of a new Registrar");
+		wps->state = SEND_WSC_NACK;
+		return WPS_CONTINUE;
+	}
+
+	wps->state = SEND_M3;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+					    struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M2D");
+
+	if (wps->state != RECV_M2) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M2D", wps->state);
+		return WPS_FAILURE;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+			  attr->manufacturer, attr->manufacturer_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+			  attr->model_name, attr->model_name_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+			  attr->model_number, attr->model_number_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+			  attr->serial_number, attr->serial_number_len);
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+			  attr->dev_name, attr->dev_name_len);
+
+	/*
+	 * TODO: notify monitor programs (cli/gui/etc.) of the M2D and provide
+	 * user information about the registrar properties.
+	 */
+
+	wps->state = RECEIVED_M2D;
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+	if (wps->state != RECV_M4) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M4", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg) ||
+	    wps_process_r_hash1(wps, attr->r_hash1) ||
+	    wps_process_r_hash2(wps, attr->r_hash2))
+		return WPS_FAILURE;
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+		   "attribute");
+	if (wps_parse_msg(decrypted, &eattr) < 0 ||
+	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
+		wpabuf_free(decrypted);
+		return WPS_FAILURE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M5;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+	if (wps->state != RECV_M6) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M6", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg))
+		return WPS_FAILURE;
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+		   "attribute");
+	if (wps_parse_msg(decrypted, &eattr) < 0 ||
+	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
+		wpabuf_free(decrypted);
+		return WPS_FAILURE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M7;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+	if (wps->state != RECV_M8) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M8", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg))
+		return WPS_FAILURE;
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+		   "attribute");
+	if (wps_parse_msg(decrypted, &eattr) < 0 ||
+	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+	    wps_process_creds(wps, eattr.cred, eattr.cred_len,
+			      eattr.num_cred) ||
+	    wps_process_ap_settings(wps, &eattr)) {
+		wpabuf_free(decrypted);
+		return WPS_FAILURE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = WPS_MSG_DONE;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	enum wps_process_res ret = WPS_CONTINUE;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	switch (*attr.msg_type) {
+	case WPS_M2:
+		ret = wps_process_m2(wps, msg, &attr);
+		break;
+	case WPS_M2D:
+		ret = wps_process_m2d(wps, &attr);
+		break;
+	case WPS_M4:
+		ret = wps_process_m4(wps, msg, &attr);
+		break;
+	case WPS_M6:
+		ret = wps_process_m6(wps, msg, &attr);
+		break;
+	case WPS_M8:
+		ret = wps_process_m8(wps, msg, &attr);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (ret == WPS_CONTINUE) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_ACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (wps->state == RECV_ACK && wps->authenticator) {
+		wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+			   "completed successfully");
+		wps->state = WPS_FINISHED;
+		return WPS_DONE;
+	}
+
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+						 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_NACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+			    attr.registrar_nonce, WPS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+			    wps->nonce_r, WPS_NONCE_LEN);
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+			    attr.enrollee_nonce, WPS_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+			    wps->nonce_e, WPS_NONCE_LEN);
+		return WPS_FAILURE;
+	}
+
+	if (attr.config_error == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+			   "in WSC_NACK");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+		   "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+
+	return WPS_FAILURE;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, u8 op_code,
+					      const struct wpabuf *msg)
+{
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+		   "op_code=%d)",
+		   (unsigned long) wpabuf_len(msg), op_code);
+
+	switch (op_code) {
+	case WSC_MSG:
+		return wps_process_wsc_msg(wps, msg);
+	case WSC_ACK:
+		return wps_process_wsc_ack(wps, msg);
+	case WSC_NACK:
+		return wps_process_wsc_nack(wps, msg);
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+		return WPS_FAILURE;
+	}
+}
+
+
+struct wpabuf * wps_enrollee_build_assoc_req_ie(void)
+{
+	struct wpabuf *ie;
+	u8 *len;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+		   "Request");
+	ie = wpabuf_alloc(100);
+	if (ie == NULL)
+		return NULL;
+
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(ie, 1);
+	wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+	if (wps_build_version(ie) ||
+	    wps_build_req_type(ie, WPS_REQ_ENROLLEE)) {
+		wpabuf_free(ie);
+		return NULL;
+	}
+
+	*len = wpabuf_len(ie) - 2;
+
+	return ie;
+}
+
+
+struct wpabuf * wps_enrollee_build_probe_req_ie(int pbc, const u8 *uuid)
+{
+	struct wpabuf *ie;
+	u8 *len;
+	u16 methods;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
+	ie = wpabuf_alloc(200);
+	if (ie == NULL)
+		return NULL;
+
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(ie, 1);
+	wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+	if (pbc)
+		methods = WPS_CONFIG_PUSHBUTTON;
+	else
+		methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
+			WPS_CONFIG_KEYPAD;
+
+	if (wps_build_version(ie) ||
+	    wps_build_req_type(ie, WPS_REQ_ENROLLEE) ||
+	    wps_build_config_methods(ie, methods) ||
+	    wps_build_uuid_e(ie, uuid) ||
+	    wps_build_primary_dev_type(NULL, ie) ||
+	    wps_build_rf_bands(NULL, ie) ||
+	    wps_build_assoc_state(NULL, ie) ||
+	    wps_build_config_error(NULL, ie) ||
+	    wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
+				      DEV_PW_DEFAULT)) {
+		wpabuf_free(ie);
+		return NULL;
+	}
+
+	*len = wpabuf_len(ie) - 2;
+
+	return ie;
+}

Added: wpasupplicant/branches/upstream/current/src/wps/wps_i.h
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_i.h?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_i.h (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_i.h Fri Feb  1 15:03:36 2008
@@ -1,0 +1,174 @@
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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
+
+#include "wps.h"
+#include "wps_defs.h"
+
+struct wps_data {
+	int authenticator;
+	struct wps_context *wps;
+	struct wps_registrar *registrar;
+	enum {
+		/* Enrollee states */
+		SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+		RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+		SEND_WSC_NACK,
+
+		/* Registrar states */
+		RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+		RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+	} state;
+
+	u8 uuid_e[WPS_UUID_LEN];
+	u8 uuid_r[WPS_UUID_LEN];
+	u8 mac_addr_e[ETH_ALEN];
+	u8 nonce_e[WPS_NONCE_LEN];
+	u8 nonce_r[WPS_NONCE_LEN];
+	u8 psk1[WPS_PSK_LEN];
+	u8 psk2[WPS_PSK_LEN];
+	u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+	u8 peer_hash1[WPS_HASH_LEN];
+	u8 peer_hash2[WPS_HASH_LEN];
+
+	struct wpabuf *dh_privkey;
+	struct wpabuf *dh_pubkey_e;
+	struct wpabuf *dh_pubkey_r;
+	u8 authkey[WPS_AUTHKEY_LEN];
+	u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+	u8 emsk[WPS_EMSK_LEN];
+
+	struct wpabuf *last_msg;
+
+	u8 *dev_password;
+	size_t dev_password_len;
+	u16 dev_pw_id;
+	int pbc;
+
+	u16 encr_type; /* available encryption types */
+	u16 auth_type; /* available authentication types */
+
+	u8 *new_psk;
+	size_t new_psk_len;
+
+	int wps_pin_revealed;
+	struct wps_credential cred;
+
+	int (*wps_cred_cb)(void *ctx, struct wps_credential *cred);
+	void *cb_ctx;
+
+	struct wps_device_data peer_dev;
+};
+
+
+struct wps_parse_attr {
+	/* fixed length fields */
+	const u8 *version; /* 1 octet */
+	const u8 *msg_type; /* 1 octet */
+	const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+	const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+	const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+	const u8 *auth_type_flags; /* 2 octets */
+	const u8 *encr_type_flags; /* 2 octets */
+	const u8 *conn_type_flags; /* 1 octet */
+	const u8 *config_methods; /* 2 octets */
+	const u8 *primary_dev_type; /* 8 octets */
+	const u8 *rf_bands; /* 1 octet */
+	const u8 *assoc_state; /* 2 octets */
+	const u8 *config_error; /* 2 octets */
+	const u8 *dev_password_id; /* 2 octets */
+	const u8 *os_version; /* 4 octets */
+	const u8 *wps_state; /* 1 octet */
+	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+	const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+	const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+	const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+	const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+	const u8 *auth_type; /* 2 octets */
+	const u8 *encr_type; /* 2 octets */
+	const u8 *network_idx; /* 1 octet */
+	const u8 *network_key_idx; /* 1 octet */
+	const u8 *mac_addr; /* ETH_ALEN (6) octets */
+	const u8 *key_prov_auto; /* 1 octet (Bool) */
+	const u8 *dot1x_enabled; /* 1 octet (Bool) */
+
+	/* variable length fields */
+	const u8 *manufacturer;
+	size_t manufacturer_len;
+	const u8 *model_name;
+	size_t model_name_len;
+	const u8 *model_number;
+	size_t model_number_len;
+	const u8 *serial_number;
+	size_t serial_number_len;
+	const u8 *dev_name;
+	size_t dev_name_len;
+	const u8 *public_key;
+	size_t public_key_len;
+	const u8 *encr_settings;
+	size_t encr_settings_len;
+	const u8 *ssid; /* <= 32 octets */
+	size_t ssid_len;
+	const u8 *network_key; /* <= 64 octets */
+	size_t network_key_len;
+	const u8 *eap_type; /* <= 8 octets */
+	size_t eap_type_len;
+	const u8 *eap_identity; /* <= 64 octets */
+	size_t eap_identity_len;
+
+	/* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+	const u8 *cred[MAX_CRED_COUNT];
+	size_t cred_len[MAX_CRED_COUNT];
+	size_t num_cred;
+};
+
+/* wps_common.c */
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len);
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
+int wps_derive_keys(struct wps_data *wps);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+			      const struct wpabuf *msg);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+		    size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+					  size_t encr_len);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+			      const u8 *key_wrap_auth);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+			    struct wpabuf *plain);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps, u8 *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps, u8 op_code,
+					      const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, u8 *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+					       u8 op_code,
+					       const struct wpabuf *msg);
+
+#endif /* WPS_I_H */

Added: wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c
URL: http://svn.debian.org/wsvn/wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c?rev=1092&op=file
==============================================================================
--- wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c (added)
+++ wpasupplicant/branches/upstream/current/src/wps/wps_registrar.c Fri Feb  1 15:03:36 2008
@@ -1,0 +1,1980 @@
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008, Jouni Malinen <j at w1.fi>
+ *
+ * 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 "sha256.h"
+#include "ieee802_11_defs.h"
+#include "eloop.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+struct wps_uuid_pin {
+	struct wps_uuid_pin *next;
+	u8 uuid[WPS_UUID_LEN];
+	u8 *pin;
+	size_t pin_len;
+	int locked;
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+	os_free(pin->pin);
+	os_free(pin);
+}
+
+
+static void wps_free_pins(struct wps_uuid_pin *pins)
+{
+	struct wps_uuid_pin *pin, *prev;
+
+	pin = pins;
+	while (pin) {
+		prev = pin;
+		pin = pin->next;
+		wps_free_pin(prev);
+	}
+}
+
+
+struct wps_pbc_session {
+	struct wps_pbc_session *next;
+	u8 addr[ETH_ALEN];
+	u8 uuid_e[WPS_UUID_LEN];
+	struct os_time timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+	struct wps_pbc_session *prev;
+
+	while (pbc) {
+		prev = pbc;
+		pbc = pbc->next;
+		os_free(prev);
+	}
+}
+
+
+struct wps_registrar {
+	struct wps_context *wps;
+
+	int pbc;
+	int selected_registrar;
+
+	int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+			  size_t psk_len);
+	int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len,
+			 const u8 *probe_resp_ie, size_t probe_resp_ie_len);
+	void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+			      const struct wps_device_data *dev);
+	void *cb_ctx;
+
+	struct wps_uuid_pin *pins;
+	struct wps_pbc_session *pbc_sessions;
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+static int wps_build_version(struct wpabuf *msg);
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+					  const u8 *addr, const u8 *uuid_e)
+{
+	struct wps_pbc_session *pbc, *prev = NULL;
+	struct os_time now;
+
+	os_get_time(&now);
+
+	pbc = reg->pbc_sessions;
+	while (pbc) {
+		if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+		    os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+			if (prev)
+				prev->next = pbc->next;
+			else
+				reg->pbc_sessions = pbc->next;
+			break;
+		}
+		prev = pbc;
+		pbc = pbc->next;
+	}
+
+	if (!pbc) {
+		pbc = os_zalloc(sizeof(*pbc));
+		if (pbc == NULL)
+			return;
+		os_memcpy(pbc->addr, addr, ETH_ALEN);
+		if (uuid_e)
+			os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+	}
+
+	pbc->next = reg->pbc_sessions;
+	reg->pbc_sessions = pbc;
+	pbc->timestamp = now;
+
+	/* remove entries that have timed out */
+	prev = pbc;
+	pbc = pbc->next;
+
+	while (pbc) {
+		if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+			prev->next = NULL;
+			wps_free_pbc_sessions(pbc);
+			break;
+		}
+		prev = pbc;
+		pbc = pbc->next;
+	}
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+					     const u8 *addr, const u8 *uuid_e)
+{
+	struct wps_pbc_session *pbc, *prev = NULL;
+
+	pbc = reg->pbc_sessions;
+	while (pbc) {
+		if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+		    os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+			if (prev)
+				prev->next = pbc->next;
+			else
+				reg->pbc_sessions = pbc->next;
+			os_free(pbc);
+			break;
+		}
+		prev = pbc;
+		pbc = pbc->next;
+	}
+}
+
+
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+			      const u8 *addr, const u8 *uuid_e)
+{
+	int count = 0;
+	struct wps_pbc_session *pbc;
+	struct os_time now;
+
+	os_get_time(&now);
+
+	for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+		if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
+			break;
+		if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
+		    uuid_e == NULL ||
+		    os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
+			count++;
+	}
+
+	if (addr || uuid_e)
+		count++;
+
+	return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
+		   wps->wps_state);
+	wpabuf_put_be16(msg, ATTR_WPS_STATE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, wps->wps_state);
+	return 0;
+}
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+				     struct wpabuf *msg)
+{
+	if (wps->ap_setup_locked) {
+		wpa_printf(MSG_DEBUG, "WPS:  * AP Setup Locked");
+		wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+		wpabuf_put_be16(msg, 1);
+		wpabuf_put_u8(msg, 1);
+	}
+	return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+					struct wpabuf *msg)
+{
+	if (!reg->selected_registrar)
+		return 0;
+	wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar");
+	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, 1);
+	return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+					     struct wpabuf *msg)
+{
+	u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+	if (!reg->selected_registrar)
+		return 0;
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
+	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, id);
+	return 0;
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+					    struct wpabuf *msg)
+{
+	u16 methods;
+	if (!reg->selected_registrar)
+		return 0;
+	methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+	if (reg->pbc)
+		methods |= WPS_CONFIG_PUSHBUTTON;
+	wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar Config Methods (%x)",
+		   methods);
+	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+					  struct wpabuf *msg)
+{
+	u16 methods;
+	methods = 0;
+	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
+	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+static int wps_build_config_methods(struct wps_registrar *reg,
+				    struct wpabuf *msg)
+{
+	u16 methods;
+	methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+	if (reg->pbc)
+		methods |= WPS_CONFIG_PUSHBUTTON;
+	wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
+	wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, methods);
+	return 0;
+}
+
+
+static int wps_build_rf_bands(struct wps_registrar *reg, struct wpabuf *msg)
+{
+	u8 bands = WPS_RF_24GHZ /* TODO: | WPS_RF_50GHZ */;
+	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", bands);
+	wpabuf_put_be16(msg, ATTR_RF_BANDS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, bands);
+	return 0;
+}
+
+
+static int wps_build_uuid_e(struct wps_registrar *reg, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * UUID-E");
+	wpabuf_put_be16(msg, ATTR_UUID_E);
+	wpabuf_put_be16(msg, WPS_UUID_LEN);
+	wpabuf_put_data(msg, reg->wps->uuid, WPS_UUID_LEN);
+	return 0;
+}
+
+
+static int wps_build_resp_type(struct wps_registrar *reg, struct wpabuf *msg)
+{
+	u8 resp = reg->wps->ap ? WPS_RESP_AP : WPS_RESP_REGISTRAR;
+	wpa_printf(MSG_DEBUG, "WPS:  * Response Type (%d)", resp);
+	wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, resp);
+	return 0;
+}
+
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+		   const struct wps_registrar_config *cfg)
+{
+	struct wps_registrar *reg = os_zalloc(sizeof(*reg));
+	if (reg == NULL)
+		return NULL;
+
+	reg->wps = wps;
+	reg->new_psk_cb = cfg->new_psk_cb;
+	reg->set_ie_cb = cfg->set_ie_cb;
+	reg->pin_needed_cb = cfg->pin_needed_cb;
+	reg->cb_ctx = cfg->cb_ctx;
+
+	if (wps_set_ie(reg)) {
+		wps_registrar_deinit(reg);
+		return NULL;
+	}
+
+	return reg;
+}
+
+
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+	if (reg == NULL)
+		return;
+	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	wps_free_pins(reg->pins);
+	wps_free_pbc_sessions(reg->pbc_sessions);
+	os_free(reg);
+}
+
+
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
+			  const u8 *pin, size_t pin_len)
+{
+	struct wps_uuid_pin *p;
+
+	p = os_zalloc(sizeof(*p));
+	if (p == NULL)
+		return -1;
+	os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+	p->pin = os_malloc(pin_len);
+	if (p->pin == NULL) {
+		os_free(p);
+		return -1;
+	}
+	os_memcpy(p->pin, pin, pin_len);
+	p->pin_len = pin_len;
+
+	p->next = reg->pins;
+	reg->pins = p;
+
+	wpa_printf(MSG_DEBUG, "WPS: A new PIN configured");
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+	reg->selected_registrar = 1;
+	reg->pbc = 0;
+	wps_set_ie(reg);
+
+	return 0;
+}
+
+
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+	struct wps_uuid_pin *pin, *prev;
+
+	prev = NULL;
+	pin = reg->pins;
+	while (pin) {
+		if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+			if (prev == NULL)
+				reg->pins = pin->next;
+			else
+				prev->next = pin->next;
+			wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+				    pin->uuid, WPS_UUID_LEN);
+			wps_free_pin(pin);
+			return 0;
+		}
+		prev = pin;
+		pin = pin->next;
+	}
+
+	return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+					const u8 *uuid, size_t *pin_len)
+{
+	struct wps_uuid_pin *pin;
+
+	pin = reg->pins;
+	while (pin) {
+		if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+			/*
+			 * Lock the PIN to avoid attacks based on concurrent
+			 * re-use of the PIN that could otherwise avoid PIN
+			 * invalidations.
+			 */
+			if (pin->locked) {
+				wpa_printf(MSG_DEBUG, "WPS: Selected PIN "
+					   "locked - do not allow concurrent "
+					   "re-use");
+				return NULL;
+			}
+			*pin_len = pin->pin_len;
+			pin->locked = 1;
+			return pin->pin;
+		}
+		pin = pin->next;
+	}
+
+	return NULL;
+}
+
+
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+	struct wps_uuid_pin *pin;
+
+	pin = reg->pins;
+	while (pin) {
+		if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+			pin->locked = 0;
+			return 0;
+		}
+		pin = pin->next;
+	}
+
+	return -1;
+}
+
+
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wps_registrar *reg = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
+	reg->selected_registrar = 0;
+	reg->pbc = 0;
+	wps_set_ie(reg);
+}
+
+
+int wps_registrar_button_pushed(struct wps_registrar *reg)
+{
+	if (wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
+			   "mode");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
+	reg->selected_registrar = 1;
+	reg->pbc = 1;
+	wps_set_ie(reg);
+
+	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+			       reg, NULL);
+	return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+	wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+	reg->selected_registrar = 0;
+	reg->pbc = 0;
+	wps_set_ie(reg);
+}
+
+
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+				const struct wpabuf *wps_data)
+{
+	struct wps_parse_attr attr;
+	u16 methods;
+
+	wpa_hexdump_buf(MSG_MSGDUMP,
+			"WPS: Probe Request with WPS data received",
+			wps_data);
+
+	if (wps_parse_msg(wps_data, &attr) < 0 ||
+	    attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
+			   "version 0x%x", attr.version ? *attr.version : 0);
+		return;
+	}
+
+	if (attr.config_methods == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
+			   "Probe Request");
+		return;
+	}
+
+	methods = WPA_GET_BE16(attr.config_methods);
+	if (!(methods & WPS_CONFIG_PUSHBUTTON))
+		return; /* Not PBC */
+
+	wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
+		   MACSTR, MAC2STR(addr));
+
+	wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+}
+
+
+static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+			  const u8 *psk, size_t psk_len)
+{
+	if (reg->new_psk_cb == NULL)
+		return 0;
+
+	return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+}
+
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+			      const struct wps_device_data *dev)
+{
+	if (reg->pin_needed_cb == NULL)
+		return;
+
+	reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg,
+			 const struct wpabuf *beacon_ie,
+			 const struct wpabuf *probe_resp_ie)
+{
+	if (reg->set_ie_cb == NULL)
+		return 0;
+
+	return reg->set_ie_cb(reg->cb_ctx, wpabuf_head(beacon_ie),
+			      wpabuf_len(beacon_ie),
+			      wpabuf_head(probe_resp_ie),
+			      wpabuf_len(probe_resp_ie));
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+	struct wpabuf *beacon;
+	struct wpabuf *probe;
+	int ret;
+	u8 *blen, *plen;
+
+	wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs");
+
+	beacon = wpabuf_alloc(300);
+	if (beacon == NULL)
+		return -1;
+	probe = wpabuf_alloc(300);
+	if (probe == NULL) {
+		wpabuf_free(beacon);
+		return -1;
+	}
+
+	wpabuf_put_u8(beacon, WLAN_EID_VENDOR_SPECIFIC);
+	blen = wpabuf_put(beacon, 1);
+	wpabuf_put_be32(beacon, WPS_DEV_OUI_WFA);
+
+	wpabuf_put_u8(probe, WLAN_EID_VENDOR_SPECIFIC);
+	plen = wpabuf_put(probe, 1);
+	wpabuf_put_be32(probe, WPS_DEV_OUI_WFA);
+
+	if (wps_build_version(beacon) ||
+	    wps_build_wps_state(reg->wps, beacon) ||
+	    wps_build_ap_setup_locked(reg->wps, beacon) ||
+	    wps_build_selected_registrar(reg, beacon) ||
+	    wps_build_sel_reg_dev_password_id(reg, beacon) ||
+	    wps_build_sel_reg_config_methods(reg, beacon) ||
+	    wps_build_version(probe) ||
+	    wps_build_wps_state(reg->wps, probe) ||
+	    wps_build_ap_setup_locked(reg->wps, probe) ||
+	    wps_build_selected_registrar(reg, probe) ||
+	    wps_build_sel_reg_dev_password_id(reg, probe) ||
+	    wps_build_sel_reg_config_methods(reg, probe) ||
+	    wps_build_resp_type(reg, probe) ||
+	    wps_build_uuid_e(reg, probe) ||
+	    wps_build_device_attrs(&reg->wps->dev, probe) ||
+	    wps_build_probe_config_methods(reg, probe) ||
+	    wps_build_rf_bands(reg, probe)) {
+		wpabuf_free(beacon);
+		wpabuf_free(probe);
+		return -1;
+	}
+
+	*blen = wpabuf_len(beacon) - 2;
+	*plen = wpabuf_len(probe) - 2;
+
+	ret = wps_cb_set_ie(reg, beacon, probe);
+	wpabuf_free(beacon);
+	wpabuf_free(probe);
+
+	return ret;
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+	const u8 *pin;
+	size_t pin_len;
+
+	os_free(wps->dev_password);
+	wps->dev_password = NULL;
+
+	if (wps->pbc) {
+		wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
+		pin = (const u8 *) "00000000";
+		pin_len = 8;
+	} else {
+		pin = wps_registrar_get_pin(wps->registrar, wps->uuid_e,
+					    &pin_len);
+	}
+	if (pin == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+			   "the Enrollee");
+		wps_cb_pin_needed(wps->registrar, wps->uuid_e, &wps->peer_dev);
+		return -1;
+	}
+
+	wps->dev_password = os_malloc(pin_len);
+	if (wps->dev_password == NULL)
+		return -1;
+	os_memcpy(wps->dev_password, pin, pin_len);
+	wps->dev_password_len = pin_len;
+
+	return 0;
+}
+
+
+static int wps_build_version(struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Version");
+	wpabuf_put_be16(msg, ATTR_VERSION);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_VERSION);
+	return 0;
+}
+
+
+static int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Message Type (%d)", msg_type);
+	wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, msg_type);
+	return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * UUID-R");
+	wpabuf_put_be16(msg, ATTR_UUID_R);
+	wpabuf_put_be16(msg, WPS_UUID_LEN);
+	wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+	return 0;
+}
+
+
+static int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Enrollee Nonce");
+	wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Registrar Nonce");
+	wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+	wpabuf_put_be16(msg, WPS_NONCE_LEN);
+	wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+	return 0;
+}
+
+
+static int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+	return 0;
+}
+
+
+static int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Connection Type Flags");
+	wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, WPS_CONN_ESS);
+	return 0;
+}
+
+
+static int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Association State");
+	wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+	return 0;
+}
+
+
+static int wps_build_dev_password_id(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID");
+	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, DEV_PW_DEFAULT);
+	return 0;
+}
+
+
+static int wps_build_config_error(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Configuration Error");
+	wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, WPS_CFG_NO_ERROR);
+	return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+	u8 *hash;
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+		return -1;
+	wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+		    wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+	if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+			   "R-Hash derivation");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * R-Hash1");
+	wpabuf_put_be16(msg, ATTR_R_HASH1);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = wps->snonce;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS:  * R-Hash2");
+	wpabuf_put_be16(msg, ATTR_R_HASH2);
+	wpabuf_put_be16(msg, SHA256_MAC_LEN);
+	hash = wpabuf_put(msg, SHA256_MAC_LEN);
+	/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+	wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+	return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce1");
+	wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce2");
+	wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+	wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+	wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+			WPS_SECRET_NONCE_LEN);
+	return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Network Index");
+	wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, 0);
+	return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, wps->wps->ssid_len);
+	wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type");
+	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, wps->auth_type);
+	return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type");
+	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, wps->encr_type);
+	return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
+	wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+	if (wps->wps->network_key) {
+		wpabuf_put_be16(msg, wps->wps->network_key_len);
+		wpabuf_put_data(msg, wps->wps->network_key,
+				wps->wps->network_key_len);
+	} else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+		char hex[65];
+		/* Generate a random per-device PSK */
+		os_free(wps->new_psk);
+		wps->new_psk_len = 32;
+		wps->new_psk = os_malloc(wps->new_psk_len);
+		if (wps->new_psk == NULL)
+			return -1;
+		if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) {
+			os_free(wps->new_psk);
+			wps->new_psk = NULL;
+			return -1;
+		}
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+				wps->new_psk, wps->new_psk_len);
+		wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+				 wps->new_psk_len);
+		wpabuf_put_be16(msg, wps->new_psk_len * 2);
+		wpabuf_put_data(msg, hex, wps->new_psk_len * 2);
+	} else {
+		/* No Network Key */
+		wpabuf_put_be16(msg, 0);
+	}
+	return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * MAC Address");
+	wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+	wpabuf_put_be16(msg, ETH_ALEN);
+	wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+	return 0;
+}
+
+
+static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+	struct wpabuf *cred;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Credential");
+
+	/* Select the best authentication and encryption type */
+	if (wps->auth_type & WPS_AUTH_WPA2PSK)
+		wps->auth_type = WPS_AUTH_WPA2PSK;
+	else if (wps->auth_type & WPS_AUTH_WPAPSK)
+		wps->auth_type = WPS_AUTH_WPAPSK;
+	else if (wps->auth_type & WPS_AUTH_OPEN)
+		wps->auth_type = WPS_AUTH_OPEN;
+	else if (wps->auth_type & WPS_AUTH_SHARED)
+		wps->auth_type = WPS_AUTH_SHARED;
+	else {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
+			   wps->auth_type);
+		return -1;
+	}
+
+	if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+	    wps->auth_type == WPS_AUTH_WPAPSK) {
+		if (wps->encr_type & WPS_ENCR_AES)
+			wps->encr_type = WPS_ENCR_AES;
+		else if (wps->encr_type & WPS_ENCR_TKIP)
+			wps->encr_type = WPS_ENCR_TKIP;
+		else {
+			wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+				   "type for WPA/WPA2");
+			return -1;
+		}
+	} else {
+		if (wps->encr_type & WPS_ENCR_WEP)
+			wps->encr_type = WPS_ENCR_WEP;
+		else if (wps->encr_type & WPS_ENCR_NONE)
+			wps->encr_type = WPS_ENCR_NONE;
+		else {
+			wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+				   "type for non-WPA/WPA2 mode");
+			return -1;
+		}
+	}
+
+	cred = wpabuf_alloc(200);
+	if (cred == NULL)
+		return -1;
+
+	if (wps_build_cred_network_idx(wps, cred) ||
+	    wps_build_cred_ssid(wps, cred) ||
+	    wps_build_cred_auth_type(wps, cred) ||
+	    wps_build_cred_encr_type(wps, cred) ||
+	    wps_build_cred_network_key(wps, cred) ||
+	    wps_build_cred_mac_addr(wps, cred)) {
+		wpabuf_free(cred);
+		return -1;
+	}
+
+	wpabuf_put_be16(msg, ATTR_CRED);
+	wpabuf_put_be16(msg, wpabuf_len(cred));
+	wpabuf_put_buf(msg, cred);
+	wpabuf_free(cred);
+
+	return 0;
+}
+
+
+static struct wpabuf * wps_build_m2(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0)
+		return NULL;
+	wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+		    wps->nonce_r, WPS_NONCE_LEN);
+	os_memcpy(wps->uuid_r, wps->wps->uuid, WPS_UUID_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M2) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_uuid_r(wps, msg) ||
+	    wps_build_public_key(wps, msg) ||
+	    wps_derive_keys(wps) ||
+	    wps_build_auth_type_flags(wps, msg) ||
+	    wps_build_encr_type_flags(wps, msg) ||
+	    wps_build_conn_type_flags(wps, msg) ||
+	    wps_build_config_methods(wps->registrar, msg) ||
+	    wps_build_device_attrs(&wps->wps->dev, msg) ||
+	    wps_build_rf_bands(wps->registrar, msg) ||
+	    wps_build_assoc_state(wps, msg) ||
+	    wps_build_config_error(wps, msg) ||
+	    wps_build_dev_password_id(wps, msg) ||
+	    wps_build_os_version(&wps->wps->dev, msg) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M3;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+	struct wpabuf *msg;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return NULL;
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M2D) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_registrar_nonce(wps, msg) ||
+	    wps_build_uuid_r(wps, msg) ||
+	    wps_build_auth_type_flags(wps, msg) ||
+	    wps_build_encr_type_flags(wps, msg) ||
+	    wps_build_conn_type_flags(wps, msg) ||
+	    wps_build_config_methods(wps->registrar, msg) ||
+	    wps_build_device_attrs(&wps->wps->dev, msg) ||
+	    wps_build_rf_bands(wps->registrar, msg) ||
+	    wps_build_assoc_state(wps, msg) ||
+	    wps_build_config_error(wps, msg) ||
+	    wps_build_os_version(&wps->wps->dev, msg)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	wps->state = RECV_M2D_ACK;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
+
+	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M4) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_r_hash(wps, msg) ||
+	    wps_build_r_snonce1(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_M5;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
+
+	plain = wpabuf_alloc(200);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M6) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_r_snonce2(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->wps_pin_revealed = 1;
+	wps->state = RECV_M7;
+	return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+	struct wpabuf *msg, *plain;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
+
+	plain = wpabuf_alloc(500);
+	if (plain == NULL)
+		return NULL;
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	if (wps_build_version(msg) ||
+	    wps_build_msg_type(msg, WPS_M8) ||
+	    wps_build_enrollee_nonce(wps, msg) ||
+	    wps_build_cred(wps, plain) ||
+	    wps_build_key_wrap_auth(wps, plain) ||
+	    wps_build_encr_settings(wps, msg, plain) ||
+	    wps_build_authenticator(wps, msg)) {
+		wpabuf_free(plain);
+		wpabuf_free(msg);
+		return NULL;
+	}
+	wpabuf_free(plain);
+
+	wps->state = RECV_DONE;
+	return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, u8 *op_code)
+{
+	struct wpabuf *msg;
+
+	switch (wps->state) {
+	case SEND_M2:
+		if (wps_get_dev_password(wps) < 0)
+			msg = wps_build_m2d(wps);
+		else
+			msg = wps_build_m2(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M2D:
+		msg = wps_build_m2d(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M4:
+		msg = wps_build_m4(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M6:
+		msg = wps_build_m6(wps);
+		*op_code = WSC_MSG;
+		break;
+	case SEND_M8:
+		msg = wps_build_m8(wps);
+		*op_code = WSC_MSG;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+			   "a message", wps->state);
+		msg = NULL;
+		break;
+	}
+
+	if (*op_code == WSC_MSG && msg) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+	if (e_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+		return -1;
+	}
+
+	os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+		    wps->nonce_e, WPS_NONCE_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+	if (r_nonce == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+		return -1;
+	}
+
+	if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+	if (uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
+		return -1;
+	}
+
+	os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+	if (pw_id == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
+		return -1;
+	}
+
+	wps->dev_pw_id = WPA_GET_BE16(pw_id);
+	wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
+
+	return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+	if (e_hash1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+	if (e_hash2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
+		return -1;
+	}
+
+	os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+	return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (e_snonce1 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+			WPS_SECRET_NONCE_LEN);
+
+	/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+	addr[0] = e_snonce1;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk1;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
+			   "not match with the pre-committed value");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
+		   "half of the device password");
+
+	return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+	u8 hash[SHA256_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+
+	if (e_snonce2 == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
+		return -1;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+			WPS_SECRET_NONCE_LEN);
+
+	/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+	addr[0] = e_snonce2;
+	len[0] = WPS_SECRET_NONCE_LEN;
+	addr[1] = wps->psk2;
+	len[1] = WPS_PSK_LEN;
+	addr[2] = wpabuf_head(wps->dh_pubkey_e);
+	len[2] = wpabuf_len(wps->dh_pubkey_e);
+	addr[3] = wpabuf_head(wps->dh_pubkey_r);
+	len[3] = wpabuf_len(wps->dh_pubkey_r);
+	hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+	if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
+			   "not match with the pre-committed value");
+		wps_registrar_invalidate_pin(wps->registrar, wps->uuid_e);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+		   "half of the device password");
+	wps->wps_pin_revealed = 0;
+	wps_registrar_unlock_pin(wps->registrar, wps->uuid_e);
+
+	return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+	if (mac_addr == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
+		   MAC2STR(mac_addr));
+	os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+	os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+	return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+			      size_t pk_len)
+{
+	if (pk == NULL || pk_len == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+		return -1;
+	}
+
+	wpabuf_free(wps->dh_pubkey_e);
+	wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+	if (wps->dh_pubkey_e == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+	u16 auth_types;
+
+	if (auth == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
+			   "received");
+		return -1;
+	}
+
+	auth_types = WPA_GET_BE16(auth);
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
+		   auth_types);
+	wps->auth_type = wps->wps->auth_types & auth_types;
+	if (wps->auth_type == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+			   "authentication types");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+	u16 encr_types;
+
+	if (encr == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
+			   "received");
+		return -1;
+	}
+
+	encr_types = WPA_GET_BE16(encr);
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
+		   encr_types);
+	wps->encr_type = wps->wps->encr_types & encr_types;
+	if (wps->encr_type == 0) {
+		wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+			   "encryption types");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+	if (conn == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
+			   "received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
+		   *conn);
+
+	return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+	u16 m;
+
+	if (methods == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
+		return -1;
+	}
+
+	m = WPA_GET_BE16(methods);
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x", m);
+
+	return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+	if (state == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
+			   "received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
+		   *state);
+
+	return 0;
+}
+
+
+static int wps_process_rf_bands(struct wps_data *wps, const u8 *bands)
+{
+	if (bands == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", *bands);
+
+	return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+	u16 a;
+
+	if (assoc == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Association State received");
+		return -1;
+	}
+
+	a = WPA_GET_BE16(assoc);
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
+
+	return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+	u16 e;
+
+	if (err == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
+		return -1;
+	}
+
+	e = WPA_GET_BE16(err);
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
+
+	return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+					   struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M1");
+
+	if (wps->state != RECV_M1) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M1", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_uuid_e(wps, attr->uuid_e) ||
+	    wps_process_mac_addr(wps, attr->mac_addr) ||
+	    wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+	    wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+	    wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+	    wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+	    wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+	    wps_process_config_methods(wps, attr->config_methods) ||
+	    wps_process_wps_state(wps, attr->wps_state) ||
+	    wps_process_device_attrs(&wps->peer_dev, attr) ||
+	    wps_process_rf_bands(wps, attr->rf_bands) ||
+	    wps_process_assoc_state(wps, attr->assoc_state) ||
+	    wps_process_dev_password_id(wps, attr->dev_password_id) ||
+	    wps_process_config_error(wps, attr->config_error) ||
+	    wps_process_os_version(&wps->peer_dev, attr->os_version))
+		return WPS_FAILURE;
+
+	if (wps->dev_pw_id != DEV_PW_DEFAULT &&
+	    wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
+	    wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
+	    wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+	    (wps->dev_pw_id != DEV_PW_PUSHBUTTON || !wps->registrar->pbc)) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
+			   wps->dev_pw_id);
+		wps->state = SEND_M2D;
+		return WPS_CONTINUE;
+	}
+
+	if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+		if (wps_registrar_pbc_overlap(wps->registrar, wps->mac_addr_e,
+					      wps->uuid_e)) {
+			wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
+				   "negotiation");
+			wps->state = SEND_M2D;
+			return WPS_CONTINUE;
+		}
+		wps_registrar_add_pbc_session(wps->registrar, wps->mac_addr_e,
+					      wps->uuid_e);
+		wps->pbc = 1;
+	}
+
+	wps->state = SEND_M2;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	wpa_printf(MSG_DEBUG, "WPS: Received M3");
+
+	if (wps->state != RECV_M3) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M3", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg) ||
+	    wps_process_e_hash1(wps, attr->e_hash1) ||
+	    wps_process_e_hash2(wps, attr->e_hash2))
+		return WPS_FAILURE;
+
+	wps->state = SEND_M4;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M5");
+
+	if (wps->state != RECV_M5) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M5", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg))
+		return WPS_FAILURE;
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+		   "attribute");
+	if (wps_parse_msg(decrypted, &eattr) < 0 ||
+	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+	    wps_process_e_snonce1(wps, eattr.e_snonce1)) {
+		wpabuf_free(decrypted);
+		return WPS_FAILURE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M6;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+					   const struct wpabuf *msg,
+					   struct wps_parse_attr *attr)
+{
+	struct wpabuf *decrypted;
+	struct wps_parse_attr eattr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received M7");
+
+	if (wps->state != RECV_M7) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving M7", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+	    wps_process_authenticator(wps, attr->authenticator, msg))
+		return WPS_FAILURE;
+
+	decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+					      attr->encr_settings_len);
+	if (decrypted == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+			   "Settings attribute");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+		   "attribute");
+	if (wps_parse_msg(decrypted, &eattr) < 0 ||
+	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+	    wps_process_e_snonce2(wps, eattr.e_snonce2)) {
+		wpabuf_free(decrypted);
+		return WPS_FAILURE;
+	}
+	wpabuf_free(decrypted);
+
+	wps->state = SEND_M8;
+	return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+	enum wps_process_res ret = WPS_CONTINUE;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_M1 &&
+	    (attr.registrar_nonce == NULL ||
+	     os_memcmp(wps->nonce_r, attr.registrar_nonce,
+		       WPS_NONCE_LEN != 0))) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	switch (*attr.msg_type) {
+	case WPS_M1:
+		ret = wps_process_m1(wps, &attr);
+		break;
+	case WPS_M3:
+		ret = wps_process_m3(wps, msg, &attr);
+		break;
+	case WPS_M5:
+		ret = wps_process_m5(wps, msg, &attr);
+		break;
+	case WPS_M7:
+		ret = wps_process_m7(wps, msg, &attr);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (ret == WPS_CONTINUE) {
+		/* Save a copy of the last message for Authenticator derivation
+		 */
+		wpabuf_free(wps->last_msg);
+		wps->last_msg = wpabuf_dup(msg);
+	}
+
+	return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+						const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_ACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (wps->state == RECV_M2D_ACK) {
+		/* TODO: support for multiple registrars and sending of
+		 * multiple M2/M2D messages */
+
+		wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
+			   "terminate negotiation");
+	}
+
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+						 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_NACK) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.config_error == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+			   "in WSC_NACK");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+		   "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+
+	return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+						 const struct wpabuf *msg)
+{
+	struct wps_parse_attr attr;
+
+	wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
+
+	if (wps->state != RECV_DONE) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+			   "receiving WSC_Done", wps->state);
+		return WPS_FAILURE;
+	}
+
+	if (wps_parse_msg(msg, &attr) < 0)
+		return WPS_FAILURE;
+
+	if (attr.version == NULL || *attr.version != WPS_VERSION) {
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+			   attr.version ? *attr.version : 0);
+		return WPS_FAILURE;
+	}
+
+	if (attr.msg_type == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+		return WPS_FAILURE;
+	}
+
+	if (*attr.msg_type != WPS_WSC_DONE) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+			   *attr.msg_type);
+		return WPS_FAILURE;
+	}
+
+	if (attr.registrar_nonce == NULL ||
+	    os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+	{
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+		return WPS_FAILURE;
+	}
+
+	if (attr.enrollee_nonce == NULL ||
+	    os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+		wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+		return WPS_FAILURE;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
+
+	if (wps->new_psk) {
+		if (wps_cb_new_psk(wps->registrar, wps->mac_addr_e,
+				   wps->new_psk, wps->new_psk_len)) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
+				   "new PSK");
+		}
+		os_free(wps->new_psk);
+		wps->new_psk = NULL;
+	}
+
+	if (wps->pbc) {
+		wps_registrar_remove_pbc_session(wps->registrar,
+						 wps->mac_addr_e, wps->uuid_e);
+		wps_registrar_pbc_completed(wps->registrar);
+	}
+
+	return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+					       u8 op_code,
+					       const struct wpabuf *msg)
+{
+
+	wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+		   "op_code=%d)",
+		   (unsigned long) wpabuf_len(msg), op_code);
+
+	switch (op_code) {
+	case WSC_MSG:
+		return wps_process_wsc_msg(wps, msg);
+	case WSC_ACK:
+		return wps_process_wsc_ack(wps, msg);
+	case WSC_NACK:
+		return wps_process_wsc_nack(wps, msg);
+	case WSC_Done:
+		return wps_process_wsc_done(wps, msg);
+	default:
+		wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+		return WPS_FAILURE;
+	}
+}




More information about the Pkg-wpa-devel mailing list