From 3f2c8ba6d39d5c5184a8fdd121998dd0d7629f1f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 27 Jun 2012 18:56:41 +0300 Subject: [PATCH] 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 --- src/wps/wps.h | 3 ++ src/wps/wps_common.c | 24 +++++++++ wpa_supplicant/config.c | 40 ++++++++++++++- wpa_supplicant/config.h | 20 ++++++++ wpa_supplicant/config_file.c | 25 +++++++++- wpa_supplicant/ctrl_iface.c | 52 ++++++++++++++++++++ wpa_supplicant/wpa_cli.c | 48 ++++++++++++++++++ wpa_supplicant/wps_supplicant.c | 87 +++++++++++++++++++++++++++++++++ wpa_supplicant/wps_supplicant.h | 2 + 9 files changed, 299 insertions(+), 2 deletions(-) diff --git a/src/wps/wps.h b/src/wps/wps.h index b49bb9128..48ba76f47 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -832,6 +832,9 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, size_t buf_len); void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); 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 */ struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index c7c0c297d..63857e0af 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -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) { struct wpabuf *data; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 80f87f474..a68b31e7b 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1860,6 +1860,9 @@ void wpa_config_free(struct wpa_config *config) os_free(config->pssid); os_free(config->p2p_pref_chan); 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); } @@ -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, struct wpa_config *config, int line, 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) _STR(f), NULL, NULL #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[] = { #ifdef CONFIG_CTRL_IFACE @@ -2884,7 +2917,11 @@ static const struct global_parse_data global_fields[] = { { FUNC(hessid), 0 }, { INT_RANGE(access_network_type, 0, 15), 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 @@ -2894,6 +2931,7 @@ static const struct global_parse_data global_fields[] = { #undef _STR #undef STR #undef STR_RANGE +#undef BIN #define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index fd6aeb381..46c4da247 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -641,6 +641,26 @@ struct wpa_config { * : */ 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; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 6d31ec1e0..8badc7b97 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -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) { FILE *f; - char buf[256], *pos; + char buf[512], *pos; int errors = 0, line = 0; struct wpa_ssid *ssid, *tail = NULL, *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 */ +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) { #ifdef CONFIG_CTRL_IFACE @@ -852,6 +869,12 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) #endif /* CONFIG_INTERWORKING */ if (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 */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index ec8c8a1e4..2ef42aca2 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -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); } + + +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 */ @@ -4048,6 +4091,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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_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 */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index cff7df8d0..ba311dff3 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -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); } + + +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 */ @@ -3041,6 +3083,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_oob", wpa_cli_cmd_wps_oob, cli_cmd_flag_sensitive, " [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, + " = create password token" }, #endif /* CONFIG_WPS_OOB */ { "wps_reg", wpa_cli_cmd_wps_reg, cli_cmd_flag_sensitive, diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 78e116695..cd16e21d7 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -11,6 +11,7 @@ #include "common.h" #include "eloop.h" #include "uuid.h" +#include "crypto/random.h" #include "crypto/dh_group5.h" #include "common/ieee802_11_defs.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; } } + + +#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 */ diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index a5472a0c1..6ce0e7bc6 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -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_in_progress(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 */