WPS: Add new mechanism for NFC config method using password token

Instead of requiring low-level access to an NFC device and synchronous
operations, the new WPS_NFC_TOKEN and WPS_NFC ctrl_iface commands can be
used to build a NFC password token and initiate WPS protocol run using
that token (or pre-configured values) as separate commands. The
WPS_NFC_TOKEN output can be written to a NFC tag using an external
program, i.e., wpa_supplicant does not need to have low-level code for
NFC operations for this.

Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2012-06-27 18:56:41 +03:00
parent 6b5a0c9466
commit 3f2c8ba6d3
9 changed files with 299 additions and 2 deletions

View file

@ -832,6 +832,9 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
size_t buf_len); size_t buf_len);
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
u16 wps_config_methods_str2bin(const char *str); u16 wps_config_methods_str2bin(const char *str);
struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
const struct wpabuf *pubkey,
const struct wpabuf *dev_pw);
/* ndef.c */ /* ndef.c */
struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);

View file

@ -335,6 +335,30 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
} }
struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
const struct wpabuf *pubkey,
const struct wpabuf *dev_pw)
{
struct wpabuf *data;
data = wpabuf_alloc(200);
if (data == NULL)
return NULL;
if (wps_build_version(data) ||
wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
wps_build_wfa_ext(data, 0, NULL, 0)) {
wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
"token");
wpabuf_free(data);
return NULL;
}
return data;
}
static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
{ {
struct wpabuf *data; struct wpabuf *data;

View file

@ -1860,6 +1860,9 @@ void wpa_config_free(struct wpa_config *config)
os_free(config->pssid); os_free(config->pssid);
os_free(config->p2p_pref_chan); os_free(config->p2p_pref_chan);
os_free(config->autoscan); os_free(config->autoscan);
wpabuf_free(config->wps_nfc_dh_pubkey);
wpabuf_free(config->wps_nfc_dh_privkey);
wpabuf_free(config->wps_nfc_dev_pw);
os_free(config); os_free(config);
} }
@ -2607,6 +2610,35 @@ static int wpa_global_config_parse_str(const struct global_parse_data *data,
} }
static int wpa_global_config_parse_bin(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
size_t len;
struct wpabuf **dst, *tmp;
len = os_strlen(pos);
if (len & 0x01)
return -1;
tmp = wpabuf_alloc(len / 2);
if (tmp == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
wpabuf_free(tmp);
return -1;
}
dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
wpabuf_free(*dst);
*dst = tmp;
wpa_printf(MSG_DEBUG, "%s", data->name);
return 0;
}
static int wpa_config_process_country(const struct global_parse_data *data, static int wpa_config_process_country(const struct global_parse_data *data,
struct wpa_config *config, int line, struct wpa_config *config, int line,
const char *pos) const char *pos)
@ -2821,6 +2853,7 @@ static int wpa_config_process_hessid(
#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f) #define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
#define STR(f) _STR(f), NULL, NULL #define STR(f) _STR(f), NULL, NULL
#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
static const struct global_parse_data global_fields[] = { static const struct global_parse_data global_fields[] = {
#ifdef CONFIG_CTRL_IFACE #ifdef CONFIG_CTRL_IFACE
@ -2884,7 +2917,11 @@ static const struct global_parse_data global_fields[] = {
{ FUNC(hessid), 0 }, { FUNC(hessid), 0 },
{ INT_RANGE(access_network_type, 0, 15), 0 }, { INT_RANGE(access_network_type, 0, 15), 0 },
{ INT_RANGE(pbc_in_m1, 0, 1), 0 }, { INT_RANGE(pbc_in_m1, 0, 1), 0 },
{ STR(autoscan), 0 } { STR(autoscan), 0 },
{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 },
{ BIN(wps_nfc_dh_pubkey), 0 },
{ BIN(wps_nfc_dh_privkey), 0 },
{ BIN(wps_nfc_dev_pw), 0 }
}; };
#undef FUNC #undef FUNC
@ -2894,6 +2931,7 @@ static const struct global_parse_data global_fields[] = {
#undef _STR #undef _STR
#undef STR #undef STR
#undef STR_RANGE #undef STR_RANGE
#undef BIN
#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) #define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))

View file

@ -641,6 +641,26 @@ struct wpa_config {
* <autoscan module name>:<module parameters> * <autoscan module name>:<module parameters>
*/ */
char *autoscan; char *autoscan;
/**
* wps_nfc_dev_pw_id - NFC Device Password ID for password token
*/
int wps_nfc_dev_pw_id;
/**
* wps_nfc_dh_pubkey - NFC DH Public Key for password token
*/
struct wpabuf *wps_nfc_dh_pubkey;
/**
* wps_nfc_dh_pubkey - NFC DH Private Key for password token
*/
struct wpabuf *wps_nfc_dh_privkey;
/**
* wps_nfc_dh_pubkey - NFC Device Password for password token
*/
struct wpabuf *wps_nfc_dev_pw;
}; };

View file

@ -320,7 +320,7 @@ static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
struct wpa_config * wpa_config_read(const char *name) struct wpa_config * wpa_config_read(const char *name)
{ {
FILE *f; FILE *f;
char buf[256], *pos; char buf[512], *pos;
int errors = 0, line = 0; int errors = 0, line = 0;
struct wpa_ssid *ssid, *tail = NULL, *head = NULL; struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL; struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL;
@ -698,6 +698,23 @@ static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
#endif /* CONFIG_NO_CONFIG_BLOBS */ #endif /* CONFIG_NO_CONFIG_BLOBS */
static void write_global_bin(FILE *f, const char *field,
const struct wpabuf *val)
{
size_t i;
const u8 *pos;
if (val == NULL)
return;
fprintf(f, "%s=", field);
pos = wpabuf_head(val);
for (i = 0; i < wpabuf_len(val); i++)
fprintf(f, "%02X", *pos++);
fprintf(f, "\n");
}
static void wpa_config_write_global(FILE *f, struct wpa_config *config) static void wpa_config_write_global(FILE *f, struct wpa_config *config)
{ {
#ifdef CONFIG_CTRL_IFACE #ifdef CONFIG_CTRL_IFACE
@ -852,6 +869,12 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
#endif /* CONFIG_INTERWORKING */ #endif /* CONFIG_INTERWORKING */
if (config->pbc_in_m1) if (config->pbc_in_m1)
fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1); fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1);
if (config->wps_nfc_dev_pw_id)
fprintf(f, "wps_nfc_dev_pw_id=%d\n",
config->wps_nfc_dev_pw_id);
write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey);
write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey);
write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
} }
#endif /* CONFIG_NO_CONFIG_WRITE */ #endif /* CONFIG_NO_CONFIG_WRITE */

View file

@ -619,6 +619,49 @@ static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
return wpas_wps_start_oob(wpa_s, cmd, path, method, name); return wpas_wps_start_oob(wpa_s, cmd, path, method, name);
} }
static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
char *cmd)
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
if (cmd == NULL || cmd[0] == '\0')
_bssid = NULL;
else if (hwaddr_aton(cmd, bssid))
return -1;
return wpas_wps_start_nfc(wpa_s, _bssid);
}
static int wpa_supplicant_ctrl_iface_wps_nfc_token(
struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
{
int ndef;
struct wpabuf *buf;
int res;
if (os_strcmp(cmd, "WPS") == 0)
ndef = 0;
else if (os_strcmp(cmd, "NDEF") == 0)
ndef = 1;
else
return -1;
buf = wpas_wps_nfc_token(wpa_s, ndef);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
#endif /* CONFIG_WPS_OOB */ #endif /* CONFIG_WPS_OOB */
@ -4048,6 +4091,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8)) if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
reply_len = -1; reply_len = -1;
} else if (os_strcmp(buf, "WPS_NFC") == 0) {
if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
wpa_s, buf + 14, reply, reply_size);
#endif /* CONFIG_WPS_OOB */ #endif /* CONFIG_WPS_OOB */
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))

View file

@ -845,6 +845,48 @@ static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
} }
return wpa_ctrl_command(ctrl, cmd); return wpa_ctrl_command(ctrl, cmd);
} }
static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
if (argc >= 1)
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC %s",
argv[0]);
else
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC");
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_NFC command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc != 1) {
printf("Invalid WPS_NFC_TOKEN command: need one argument:\n"
"format: WPS or NDEF\n");
return -1;
}
if (argc >= 1)
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s",
argv[0]);
else
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN");
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_NFC_TOKEN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
#endif /* CONFIG_WPS_OOB */ #endif /* CONFIG_WPS_OOB */
@ -3041,6 +3083,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
{ "wps_oob", wpa_cli_cmd_wps_oob, { "wps_oob", wpa_cli_cmd_wps_oob,
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,
"<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" }, "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" },
{ "wps_nfc", wpa_cli_cmd_wps_nfc,
cli_cmd_flag_none,
"[BSSID] = start Wi-Fi Protected Setup: NFC" },
{ "wps_nfc_token", wpa_cli_cmd_wps_nfc_token,
cli_cmd_flag_none,
"<WPS|NDEF> = create password token" },
#endif /* CONFIG_WPS_OOB */ #endif /* CONFIG_WPS_OOB */
{ "wps_reg", wpa_cli_cmd_wps_reg, { "wps_reg", wpa_cli_cmd_wps_reg,
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,

View file

@ -11,6 +11,7 @@
#include "common.h" #include "common.h"
#include "eloop.h" #include "eloop.h"
#include "uuid.h" #include "uuid.h"
#include "crypto/random.h"
#include "crypto/dh_group5.h" #include "crypto/dh_group5.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h" #include "common/ieee802_11_common.h"
@ -1760,3 +1761,89 @@ void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
wps->dev.serial_number = wpa_s->conf->serial_number; wps->dev.serial_number = wpa_s->conf->serial_number;
} }
} }
#ifdef CONFIG_WPS_NFC
struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
{
struct wpabuf *priv = NULL, *pub = NULL, *pw;
void *dh_ctx;
struct wpabuf *ret;
pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
if (pw == NULL)
return NULL;
if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
WPS_OOB_DEVICE_PASSWORD_LEN)) {
wpabuf_free(pw);
return NULL;
}
dh_ctx = dh5_init(&priv, &pub);
if (dh_ctx == NULL) {
wpabuf_free(pw);
return NULL;
}
dh5_free(dh_ctx);
wpa_s->conf->wps_nfc_dev_pw_id = 0x10 + os_random() % 0xfff0;
wpabuf_free(wpa_s->conf->wps_nfc_dh_pubkey);
wpa_s->conf->wps_nfc_dh_pubkey = pub;
wpabuf_free(wpa_s->conf->wps_nfc_dh_privkey);
wpa_s->conf->wps_nfc_dh_privkey = priv;
wpabuf_free(wpa_s->conf->wps_nfc_dev_pw);
wpa_s->conf->wps_nfc_dev_pw = pw;
ret = wps_build_nfc_pw_token(wpa_s->conf->wps_nfc_dev_pw_id,
wpa_s->conf->wps_nfc_dh_pubkey,
wpa_s->conf->wps_nfc_dev_pw);
if (ndef) {
struct wpabuf *tmp;
tmp = ndef_build_wifi(ret);
wpabuf_free(ret);
if (tmp == NULL)
return NULL;
ret = tmp;
}
return ret;
}
int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wps_context *wps = wpa_s->wps;
char pw[32 * 2 + 1];
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
wpa_s->conf->wps_nfc_dh_privkey == NULL ||
wpa_s->conf->wps_nfc_dev_pw == NULL)
return -1;
dh5_free(wps->dh_ctx);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
wps->dh_ctx = NULL;
wpabuf_free(wps->dh_pubkey);
wps->dh_pubkey = NULL;
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = NULL;
return -1;
}
wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
if (wps->dh_ctx == NULL)
return -1;
wpa_snprintf_hex_uppercase(pw, sizeof(pw),
wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
wpa_s->conf->wps_nfc_dev_pw_id);
}
#endif /* CONFIG_WPS_NFC */

View file

@ -62,6 +62,8 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s); int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
int wpas_wps_in_progress(struct wpa_supplicant *wpa_s); int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
void wpas_wps_update_config(struct wpa_supplicant *wpa_s); void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
#else /* CONFIG_WPS */ #else /* CONFIG_WPS */