Browse Source

WPS: Add UFD support (USBA out-of-band mechanism)

This patch is only for the following use case:
- Enrollee = wpa_supplicant
- Registrar = hostapd internal Registrar

Following UFD methods can be used:
- Enrollee PIN with UFD
- Registrar PIN with UFD
- unencrypted credential with UFD

Encrypted credentials are not supported.

Enrollee side operation:
wpa_cli -i ath0 wps_oob <device type> <mount point> <oob method>
    oob method = pin-e/pin-r/cred

wpa_cli -i ath0 wps_oob ufd /mnt/ pin-r

Registrar side operation:
./hostapd_cli -i ath0 wps_oob <device type> <mount point> <oob method>
    oob method = pin-e/pin-r/cred

hostapd_cli -i ath0 wps_oob ufd /mnt/ cred
Masashi Honma 16 years ago
parent
commit
46bdb83acd

+ 1 - 0
hostapd/Makefile

@@ -313,6 +313,7 @@ OBJS += ../src/wps/wps_attr_process.o
 OBJS += ../src/wps/wps_dev_attr.o
 OBJS += ../src/wps/wps_enrollee.o
 OBJS += ../src/wps/wps_registrar.o
+OBJS += ../src/wps/wps_ufd.o
 NEED_DH_GROUPS=y
 NEED_SHA256=y
 NEED_CRYPTO=y

+ 21 - 0
hostapd/ctrl_iface.c

@@ -252,6 +252,24 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
 	*pin++ = '\0';
 	return hostapd_wps_add_pin(hapd, txt, pin);
 }
+
+
+static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
+{
+	char *path, *method;
+
+	path = os_strchr(txt, ' ');
+	if (path == NULL)
+		return -1;
+	*path++ = '\0';
+
+	method = os_strchr(path, ' ');
+	if (method == NULL)
+		return -1;
+	*method++ = '\0';
+
+	return hostapd_wps_start_oob(hapd, txt, path, method);
+}
 #endif /* CONFIG_WPS */
 
 
@@ -350,6 +368,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
 	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
 		if (hostapd_wps_button_pushed(hapd))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
+		if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
+			reply_len = -1;
 #endif /* CONFIG_WPS */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);

+ 27 - 0
hostapd/hostapd_cli.c

@@ -89,6 +89,7 @@ static const char *commands_help =
 #ifdef CONFIG_WPS
 "   wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
 "   wps_pbc              indicate button pushed to initiate PBC\n"
+"   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
 #endif /* CONFIG_WPS */
 "   help                 show this usage help\n"
 "   interface [ifname]   show interfaces/select interface\n"
@@ -275,6 +276,31 @@ static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
 {
 	return wpa_ctrl_command(ctrl, "WPS_PBC");
 }
+
+
+static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc != 3) {
+		printf("Invalid WPS_OOB command: need three arguments:\n"
+		       "- OOB_DEV_TYPE: use 'ufd'\n"
+		       "- OOB_PATH: path of OOB device like '/mnt'\n"
+		       "- OOB_METHOD: OOB method 'pin-e' or 'pin-r', "
+		       "'cred'\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
+			  argv[0], argv[1], argv[2]);
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long WPS_OOB command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
 #endif /* CONFIG_WPS */
 
 
@@ -432,6 +458,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
 #ifdef CONFIG_WPS
 	{ "wps_pin", hostapd_cli_cmd_wps_pin },
 	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
+	{ "wps_oob", hostapd_cli_cmd_wps_oob },
 #endif /* CONFIG_WPS */
 	{ "help", hostapd_cli_cmd_help },
 	{ "interface", hostapd_cli_cmd_interface },

+ 37 - 0
hostapd/wps_hostapd.c

@@ -26,6 +26,7 @@
 #include "wps/wps_defs.h"
 #include "wps/wps_dev_attr.h"
 #include "wps_hostapd.h"
+#include "dh_groups.h"
 
 
 #ifdef CONFIG_WPS_UPNP
@@ -648,6 +649,16 @@ int hostapd_init_wps(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_WPS_UPNP */
 
+	wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+				 &wps->dh_privkey);
+	wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+	if (wps->dh_pubkey == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+			   "Diffie-Hellman handshake");
+		os_free(wps);
+		return -1;
+	}
+
 	hapd->wps = wps;
 
 	return 0;
@@ -664,6 +675,8 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
 	wps_registrar_deinit(hapd->wps->registrar);
 	os_free(hapd->wps->network_key);
 	wps_device_data_free(&hapd->wps->dev);
+	wpabuf_free(hapd->wps->dh_pubkey);
+	wpabuf_free(hapd->wps->dh_privkey);
 	wps_free_pending_msgs(hapd->wps->upnp_msgs);
 	os_free(hapd->wps);
 	hapd->wps = NULL;
@@ -696,6 +709,30 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd)
 }
 
 
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+			  char *path, char *method)
+{
+	struct wps_context *wps = hapd->wps;
+
+	wps->oob_dev = wps_get_oob_device(device_type);
+	if (wps->oob_dev == NULL)
+		return -1;
+	wps->oob_dev->device_path = path;
+	wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+	if (wps_process_oob(wps, 1) < 0)
+		return -1;
+
+	if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+	     wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+	    hostapd_wps_add_pin(hapd, "any",
+				wpabuf_head(wps->oob_conf.dev_password)) < 0)
+			return -1;
+
+	return 0;
+}
+
+
 void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
 			      const u8 *ie, size_t ie_len)
 {

+ 2 - 0
hostapd/wps_hostapd.h

@@ -23,6 +23,8 @@ void hostapd_deinit_wps(struct hostapd_data *hapd);
 int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
 			const char *pin);
 int hostapd_wps_button_pushed(struct hostapd_data *hapd);
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+			  char *path, char *method);
 void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr,
 			      const u8 *ie, size_t ie_len);
 

+ 3 - 2
src/wps/wps.c

@@ -44,7 +44,8 @@ struct wps_data * wps_init(const struct wps_config *cfg)
 		os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
 	}
 	if (cfg->pin) {
-		data->dev_pw_id = DEV_PW_DEFAULT;
+		data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
+			DEV_PW_DEFAULT : data->wps->oob_dev_pw_id;
 		data->dev_password = os_malloc(cfg->pin_len);
 		if (data->dev_password == NULL) {
 			os_free(data);
@@ -300,7 +301,7 @@ struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
 		methods = WPS_CONFIG_PUSHBUTTON;
 	else
 		methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
-			WPS_CONFIG_KEYPAD;
+			WPS_CONFIG_KEYPAD | WPS_CONFIG_USBA;
 
 	if (wps_build_version(ie) ||
 	    wps_build_req_type(ie, req_type) ||

+ 47 - 0
src/wps/wps.h

@@ -88,6 +88,17 @@ struct wps_device_data {
 	u8 rf_bands;
 };
 
+struct oob_conf_data {
+	enum {
+		OOB_METHOD_UNKNOWN = 0,
+		OOB_METHOD_DEV_PWD_E,
+		OOB_METHOD_DEV_PWD_R,
+		OOB_METHOD_CRED,
+	} oob_method;
+	struct wpabuf *dev_password;
+	struct wpabuf *pubkey_hash;
+};
+
 /**
  * struct wps_config - WPS configuration for a single registration protocol run
  */
@@ -397,6 +408,31 @@ struct wps_context {
 	 */
 	struct wps_device_data dev;
 
+	/**
+	 * oob_dev - OOB Device data
+	 */
+	struct oob_device_data *oob_dev;
+
+	/**
+	 * oob_conf - OOB Config data
+	 */
+	struct oob_conf_data oob_conf;
+
+	/**
+	 * oob_dev_pw_id - OOB Device password id
+	 */
+	u16 oob_dev_pw_id;
+
+	/**
+	 * dh_privkey - Diffie-Hellman private key
+	 */
+	struct wpabuf *dh_privkey;
+
+	/**
+	 * dh_pubkey_oob - Diffie-Hellman public key
+	 */
+	struct wpabuf *dh_pubkey;
+
 	/**
 	 * config_methods - Enabled configuration methods
 	 *
@@ -494,6 +530,13 @@ struct wps_context {
 	struct upnp_pending_message *upnp_msgs;
 };
 
+struct oob_device_data {
+	char *device_path;
+	int (*init_func)(struct wps_context *, int);
+	struct wpabuf * (*read_func)(void);
+	int (*write_func)(struct wpabuf *);
+	int (*deinit_func)(void);
+};
 
 struct wps_registrar *
 wps_registrar_init(struct wps_context *wps,
@@ -515,4 +558,8 @@ unsigned int wps_pin_valid(unsigned int pin);
 unsigned int wps_generate_pin(void);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
+struct oob_device_data * wps_get_oob_device(char *device_type);
+int wps_get_oob_method(char *method);
+int wps_process_oob(struct wps_context *wps, int registrar);
+
 #endif /* WPS_H */

+ 48 - 4
src/wps/wps_attr_build.c

@@ -15,7 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
-#include "dh_groups.h"
+#include "crypto.h"
 #include "sha256.h"
 #include "aes_wrap.h"
 #include "wps_i.h"
@@ -26,11 +26,13 @@ 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);
-	pubkey = wpabuf_zeropad(pubkey, 192);
-	if (pubkey == NULL) {
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+	pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+	if (wps->dh_privkey == NULL || pubkey == NULL) {
 		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
 			   "Diffie-Hellman handshake");
+		wpabuf_free(pubkey);
 		return -1;
 	}
 
@@ -252,3 +254,45 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
 
 	return 0;
 }
+
+
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+{
+	size_t hash_len;
+	const u8 *addr[1];
+	u8 pubkey_hash[WPS_HASH_LEN];
+	u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
+
+	wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password");
+
+	addr[0] = wpabuf_head(wps->dh_pubkey);
+	hash_len = wpabuf_len(wps->dh_pubkey);
+	sha256_vector(1, addr, &hash_len, pubkey_hash);
+
+	if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
+		wpa_printf(MSG_ERROR, "WPS: device password id "
+			   "generation error");
+		return -1;
+	}
+	wps->oob_dev_pw_id |= 0x0010;
+
+	if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
+		wpa_printf(MSG_ERROR, "WPS: OOB device password "
+			   "generation error");
+		return -1;
+	}
+
+	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+	wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	wpabuf_put_be16(msg, wps->oob_dev_pw_id);
+	wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+	wpa_snprintf_hex_uppercase(
+		wpabuf_put(wps->oob_conf.dev_password,
+			   wpabuf_size(wps->oob_conf.dev_password)),
+		wpabuf_size(wps->oob_conf.dev_password),
+		dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+	return 0;
+}

+ 8 - 0
src/wps/wps_attr_parse.c

@@ -150,6 +150,14 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
 		}
 		attr->dev_password_id = pos;
 		break;
+	case ATTR_OOB_DEVICE_PASSWORD:
+		if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
+				   "Password length %u", len);
+			return -1;
+		}
+		attr->oob_dev_password = pos;
+		break;
 	case ATTR_OS_VERSION:
 		if (len != 4) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "

+ 200 - 0
src/wps/wps_common.c

@@ -335,3 +335,203 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
 	data.pwd_auth_fail.part = part;
 	wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
 }
+
+
+static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+	struct wps_data data;
+	struct wpabuf *plain;
+
+	plain = wpabuf_alloc(500);
+	if (plain == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+			   "credential");
+		return NULL;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.wps = wps;
+	data.auth_type = wps->auth_types;
+	data.encr_type = wps->encr_types;
+	if (wps_build_version(plain) || wps_build_cred(&data, plain)) {
+		wpabuf_free(plain);
+		return NULL;
+	}
+
+	return plain;
+}
+
+
+static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
+{
+	struct wpabuf *data;
+
+	data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+	if (data == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+			   "device password attribute");
+		return NULL;
+	}
+
+	wpabuf_free(wps->oob_conf.dev_password);
+	wps->oob_conf.dev_password =
+		wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+	if (wps->oob_conf.dev_password == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+			   "device password");
+		wpabuf_free(data);
+		return NULL;
+	}
+
+	if (wps_build_version(data) ||
+	    wps_build_oob_dev_password(data, wps)) {
+		wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
+			   "attribute error");
+		wpabuf_free(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+
+static int wps_parse_oob_dev_pwd(struct wps_context *wps,
+				 struct wpabuf *data)
+{
+	struct oob_conf_data *oob_conf = &wps->oob_conf;
+	struct wps_parse_attr attr;
+	const u8 *pos;
+
+	if (wps_parse_msg(data, &attr) < 0 ||
+	    attr.oob_dev_password == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
+		return -1;
+	}
+
+	pos = attr.oob_dev_password;
+
+	oob_conf->pubkey_hash =
+		wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
+	if (oob_conf->pubkey_hash == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+			   "public key hash");
+		return -1;
+	}
+	pos += WPS_OOB_PUBKEY_HASH_LEN;
+
+	wps->oob_dev_pw_id = WPA_GET_BE16(pos);
+	pos += sizeof(wps->oob_dev_pw_id);
+
+	oob_conf->dev_password =
+		wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+	if (oob_conf->dev_password == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+			   "device password");
+		return -1;
+	}
+	wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
+				   wpabuf_size(oob_conf->dev_password)),
+				   wpabuf_size(oob_conf->dev_password), pos,
+				   WPS_OOB_DEVICE_PASSWORD_LEN);
+
+	return 0;
+}
+
+
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+{
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	size_t i;
+
+	if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+		wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
+		return -1;
+	}
+
+	for (i = 0; i < attr.num_cred; i++) {
+		struct wps_credential local_cred;
+		struct wps_parse_attr cattr;
+
+		os_memset(&local_cred, 0, sizeof(local_cred));
+		wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+		if (wps_parse_msg(&msg, &cattr) < 0 ||
+		    wps_process_cred(&cattr, &local_cred)) {
+			wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+				   "credential");
+			return -1;
+		}
+		wps->cred_cb(wps->cb_ctx, &local_cred);
+	}
+
+	return 0;
+}
+
+
+int wps_process_oob(struct wps_context *wps, int registrar)
+{
+	struct oob_device_data *oob_dev = wps->oob_dev;
+	struct wpabuf *data;
+	int ret, write_f, oob_method = wps->oob_conf.oob_method;
+
+	write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
+
+	if (oob_dev->init_func(wps, registrar) < 0) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
+		return -1;
+	}
+
+	if (write_f) {
+		if (oob_method == OOB_METHOD_CRED)
+			data = wps_get_oob_cred(wps);
+		else
+			data = wps_get_oob_dev_pwd(wps);
+
+		ret = 0;
+		if (data == NULL || wps->oob_dev->write_func(data) < 0)
+			ret = -1;
+	} else {
+		data = oob_dev->read_func();
+		if (data == NULL)
+			return -1;
+
+		if (oob_method == OOB_METHOD_CRED)
+			ret = wps_parse_oob_cred(wps, data);
+		else
+			ret = wps_parse_oob_dev_pwd(wps, data);
+	}
+	wpabuf_free(data);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
+		return -1;
+	}
+
+	if (oob_dev->deinit_func() < 0) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to deinitialize OOB "
+			   "device");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+struct oob_device_data * wps_get_oob_device(char *device_type)
+{
+	if (os_strstr(device_type, "ufd") != NULL)
+		return &oob_ufd_device_data;
+
+	return NULL;
+}
+
+
+int wps_get_oob_method(char *method)
+{
+	if (os_strstr(method, "pin-e") != NULL)
+		return OOB_METHOD_DEV_PWD_E;
+	if (os_strstr(method, "pin-r") != NULL)
+		return OOB_METHOD_DEV_PWD_R;
+	if (os_strstr(method, "cred") != NULL)
+		return OOB_METHOD_CRED;
+	return OOB_METHOD_UNKNOWN;
+}

+ 3 - 0
src/wps/wps_defs.h

@@ -33,6 +33,9 @@
 #define WPS_MGMTAUTHKEY_LEN 32
 #define WPS_MGMTENCKEY_LEN 16
 #define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
 
 /* Attribute Types */
 enum wps_attribute {

+ 17 - 1
src/wps/wps_enrollee.c

@@ -18,6 +18,7 @@
 #include "sha256.h"
 #include "wps_i.h"
 #include "wps_dev_attr.h"
+#include "crypto.h"
 
 
 static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
@@ -130,7 +131,8 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
 	if (msg == NULL)
 		return NULL;
 
-	methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+	methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+		  WPS_CONFIG_USBA;
 	if (wps->pbc)
 		methods |= WPS_CONFIG_PUSHBUTTON;
 
@@ -513,6 +515,20 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
 		return -1;
 	}
 
+	if (wps->wps->oob_conf.pubkey_hash != NULL) {
+		const u8 *addr[1];
+		u8 hash[WPS_HASH_LEN];
+
+		addr[0] = pk;
+		sha256_vector(1, addr, &pk_len, hash);
+		if (os_memcmp(hash,
+			      wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+			      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+			wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+			return -1;
+		}
+	}
+
 	wpabuf_free(wps->dh_pubkey_r);
 	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
 	if (wps->dh_pubkey_r == NULL)

+ 6 - 0
src/wps/wps_i.h

@@ -124,6 +124,8 @@ struct wps_parse_attr {
 	const u8 *assoc_state; /* 2 octets */
 	const u8 *config_error; /* 2 octets */
 	const u8 *dev_password_id; /* 2 octets */
+	const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
+				     * octets */
 	const u8 *os_version; /* 4 octets */
 	const u8 *wps_state; /* 1 octet */
 	const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
@@ -191,6 +193,8 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
 void wps_success_event(struct wps_context *wps);
 void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
 
+extern struct oob_device_data oob_ufd_device_data;
+
 /* wps_attr_parse.c */
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
 
@@ -213,6 +217,7 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
 int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
 
 /* wps_attr_process.c */
 int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -237,6 +242,7 @@ struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
 enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
 					       enum wsc_op_code op_code,
 					       const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
 
 
 static inline int wps_version_supported(const u8 *version)

+ 26 - 2
src/wps/wps_registrar.c

@@ -22,6 +22,7 @@
 #include "wps_i.h"
 #include "wps_dev_attr.h"
 #include "wps_upnp.h"
+#include "crypto.h"
 
 
 struct wps_uuid_pin {
@@ -981,7 +982,7 @@ static int wps_build_credential(struct wpabuf *msg,
 }
 
 
-static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 {
 	struct wpabuf *cred;
 
@@ -1626,6 +1627,20 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
 		return -1;
 	}
 
+	if (wps->wps->oob_conf.pubkey_hash != NULL) {
+		const u8 *addr[1];
+		u8 hash[WPS_HASH_LEN];
+
+		addr[0] = pk;
+		sha256_vector(1, addr, &pk_len, hash);
+		if (os_memcmp(hash,
+			      wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+			      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+			wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+			return -1;
+		}
+	}
+
 	wpabuf_free(wps->dh_pubkey_e);
 	wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
 	if (wps->dh_pubkey_e == NULL)
@@ -1793,7 +1808,8 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
 	    wps_process_os_version(&wps->peer_dev, attr->os_version))
 		return WPS_FAILURE;
 
-	if (wps->dev_pw_id != DEV_PW_DEFAULT &&
+	if (wps->dev_pw_id < 0x10 &&
+	    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 &&
@@ -1805,6 +1821,14 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
 		return WPS_CONTINUE;
 	}
 
+	if (wps->dev_pw_id >= 0x10 &&
+	    wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
+		wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
+			   "%d mismatch", 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->wps->registrar,
 					      wps->mac_addr_e, wps->uuid_e)) {

+ 193 - 0
src/wps/wps_ufd.c

@@ -0,0 +1,193 @@
+/*
+ * UFD routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "wps/wps.h"
+
+static int ufd_fd = -1;
+
+
+static int dev_pwd_e_file_filter(const struct dirent *entry)
+{
+	unsigned int prefix;
+	char ext[5];
+
+	if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
+		return 0;
+	if (prefix == 0)
+		return 0;
+	if (os_strcasecmp(ext, "WFA") != 0)
+		return 0;
+
+	return 1;
+}
+
+
+static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
+{
+	struct dirent **namelist;
+	int i, file_num;
+
+	file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
+			   alphasort);
+	if (file_num <= 0) {
+		wpa_printf(MSG_ERROR, "WPS: OOB file not found");
+		return -1;
+	}
+	os_strlcpy(file_name, namelist[0]->d_name, 13);
+	for (i = 0; i < file_num; i++)
+		os_free(namelist[i]);
+	os_free(namelist);
+	return 0;
+}
+
+
+static int get_file_name(struct wps_context *wps, int registrar,
+			 char *file_name)
+{
+	switch (wps->oob_conf.oob_method) {
+	case OOB_METHOD_CRED:
+		os_snprintf(file_name, 13, "00000000.WSC");
+		break;
+	case OOB_METHOD_DEV_PWD_E:
+		if (registrar) {
+			char temp[128];
+
+			os_snprintf(temp, sizeof(temp), "%s/SMRTNTKY/WFAWSC",
+				wps->oob_dev->device_path);
+			if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
+				return -1;
+		} else {
+			u8 *mac_addr = wps->dev.mac_addr;
+
+			os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
+				    mac_addr[2], mac_addr[3], mac_addr[4],
+				    mac_addr[5]);
+		}
+		break;
+	case OOB_METHOD_DEV_PWD_R:
+		os_snprintf(file_name, 13, "00000000.WFA");
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int init_ufd(struct wps_context *wps, int registrar)
+{
+	int write_f;
+	char temp[128];
+	char *path = wps->oob_dev->device_path;
+	char filename[13];
+
+	write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
+		!registrar : registrar;
+
+	if (get_file_name(wps, registrar, filename) < 0) {
+		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
+		return -1;
+	}
+
+	if (write_f) {
+		os_snprintf(temp, sizeof(temp),
+			    "mkdir -p %s/SMRTNTKY/WFAWSC", path);
+		if (system(temp) != 0) {
+			wpa_printf(MSG_ERROR, "WPS (UFD): Failed "
+				   "to mkdir");
+			return -1;
+		}
+	}
+
+	os_snprintf(temp, sizeof(temp), "%s/SMRTNTKY/WFAWSC/%s", path,
+		    filename);
+	if (write_f)
+		ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
+			      S_IRUSR | S_IWUSR);
+	else
+		ufd_fd = open(temp, O_RDONLY);
+	if (ufd_fd < 0) {
+		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
+			   temp, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf * read_ufd(void)
+{
+	struct wpabuf *buf;
+	struct stat s;
+	size_t file_size;
+
+	if (fstat(ufd_fd, &s) < 0) {
+		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
+		return NULL;
+	}
+
+	file_size = s.st_size;
+	buf = wpabuf_alloc(file_size);
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
+			   "buffer");
+		return NULL;
+	}
+
+	if (read(ufd_fd, wpabuf_mhead(buf), file_size) != (int) file_size) {
+		wpabuf_free(buf);
+		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
+		return NULL;
+	}
+	wpabuf_put(buf, file_size);
+	return buf;
+}
+
+
+static int write_ufd(struct wpabuf *buf)
+{
+	if (write(ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
+	    (int) wpabuf_len(buf)) {
+		wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int deinit_ufd(void)
+{
+	close(ufd_fd);
+	ufd_fd = -1;
+	return 0;
+}
+
+
+struct oob_device_data oob_ufd_device_data = {
+	.device_path	= NULL,
+	.init_func	= init_ufd,
+	.read_func	= read_ufd,
+	.write_func	= write_ufd,
+	.deinit_func	= deinit_ufd,
+};

+ 1 - 0
wpa_supplicant/Makefile

@@ -521,6 +521,7 @@ OBJS += ../src/wps/wps_attr_process.o
 OBJS += ../src/wps/wps_dev_attr.o
 OBJS += ../src/wps/wps_enrollee.o
 OBJS += ../src/wps/wps_registrar.o
+OBJS += ../src/wps/wps_ufd.o
 OBJS_h += ../src/eap_server/eap_wsc.o
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y

+ 22 - 0
wpa_supplicant/ctrl_iface.c

@@ -204,6 +204,25 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
+					     char *cmd)
+{
+	char *path, *method;
+
+	path = os_strchr(cmd, ' ');
+	if (path == NULL)
+		return -1;
+	*path++ = '\0';
+
+	method = os_strchr(path, ' ');
+	if (method == NULL)
+		return -1;
+	*method++ = '\0';
+
+	return wpas_wps_start_oob(wpa_s, cmd, path, method);
+}
+
+
 static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
 					     char *cmd)
 {
@@ -1583,6 +1602,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 		reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
 							      reply,
 							      reply_size);
+	} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
+		if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
 		if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
 			reply_len = -1;

+ 27 - 0
wpa_supplicant/wpa_cli.c

@@ -446,6 +446,30 @@ static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc != 3) {
+		printf("Invalid WPS_OOB command: need three arguments:\n"
+		       "- OOB_DEV_TYPE: use 'ufd'\n"
+		       "- OOB_PATH: path of OOB device like '/mnt'\n"
+		       "- OOB_METHOD: OOB method 'pin-e' or 'pin-r', "
+		       "'cred'\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
+			  argv[0], argv[1], argv[2]);
+	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		printf("Too long WPS_OOB command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -1258,6 +1282,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
 	  cli_cmd_flag_sensitive,
 	  "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
 	  "hardcoded)" },
+	{ "wps_oob", wpa_cli_cmd_wps_oob,
+	  cli_cmd_flag_sensitive,
+	  "<OOB_DEV_TYPE> <OOB_PATH> <OOB_METHOD> = start WPS OOB" },
 	{ "wps_reg", wpa_cli_cmd_wps_reg,
 	  cli_cmd_flag_sensitive,
 	  "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },

+ 43 - 1
wpa_supplicant/wps_supplicant.c

@@ -27,6 +27,7 @@
 #include "eap_common/eap_wsc_common.h"
 #include "blacklist.h"
 #include "wps_supplicant.h"
+#include "dh_groups.h"
 
 #define WPS_PIN_SCAN_IGNORE_SEL_REG 3
 
@@ -440,7 +441,7 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin)
 {
 	struct wpa_ssid *ssid;
-	char val[30];
+	char val[128];
 	unsigned int rpin = 0;
 
 	wpas_clear_wps(wpa_s);
@@ -461,6 +462,33 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 }
 
 
+int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
+		       char *path, char *method)
+{
+	struct wps_context *wps = wpa_s->wps;
+
+	wps->oob_dev = wps_get_oob_device(device_type);
+	if (wps->oob_dev == NULL)
+		return -1;
+	wps->oob_dev->device_path = path;
+	wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+	if (wps->oob_conf.oob_method == OOB_METHOD_CRED)
+		wpas_clear_wps(wpa_s);
+
+	if (wps_process_oob(wps, 0) < 0)
+		return -1;
+
+	if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+	     wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+	    wpas_wps_start_pin(wpa_s, NULL,
+			       wpabuf_head(wps->oob_conf.dev_password)) < 0)
+			return -1;
+
+	return 0;
+}
+
+
 int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin)
 {
@@ -584,6 +612,16 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
 		return -1;
 	}
 
+	wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+				 &wps->dh_privkey);
+	wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+	if (wps->dh_pubkey == NULL) {
+		wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+			   "Diffie-Hellman handshake");
+		os_free(wps);
+		return -1;
+	}
+
 	wpa_s->wps = wps;
 
 	return 0;
@@ -598,6 +636,10 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 		return;
 
 	wps_registrar_deinit(wpa_s->wps->registrar);
+	wpabuf_free(wpa_s->wps->dh_pubkey);
+	wpabuf_free(wpa_s->wps->dh_privkey);
+	wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
+	wpabuf_free(wpa_s->wps->oob_conf.dev_password);
 	os_free(wpa_s->wps->network_key);
 	os_free(wpa_s->wps);
 	wpa_s->wps = NULL;

+ 2 - 0
wpa_supplicant/wps_supplicant.h

@@ -27,6 +27,8 @@ enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
 int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin);
+int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
+		       char *path, char *method);
 int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin);
 int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,