WPS: Add preliminary NFC connection handover support for Enrollee
authorJouni Malinen <j@w1.fi>
Sun, 28 Oct 2012 15:39:46 +0000 (17:39 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 28 Oct 2012 15:39:46 +0000 (17:39 +0200)
This commit adds new wpa_supplicant ctrl_iface commands to allow
external programs to go through NFC connection handover mechanism
with wpa_supplicant taking care of the WPS processing. This version
includes only the case where wpa_supplicant is operating as a
station/Enrollee.

Signed-hostap: Jouni Malinen <j@w1.fi>

src/wps/ndef.c
src/wps/wps.h
wpa_supplicant/README-WPS
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wps_supplicant.c
wpa_supplicant/wps_supplicant.h

index 7630ecb..acbf115 100644 (file)
@@ -17,6 +17,7 @@
 #define FLAG_CHUNK (1 << 5)
 #define FLAG_SHORT_RECORD (1 << 4)
 #define FLAG_ID_LENGTH_PRESENT (1 << 3)
+#define FLAG_TNF_NFC_FORUM (0x01)
 #define FLAG_TNF_RFC2046 (0x02)
 
 struct ndef_record {
@@ -168,3 +169,78 @@ struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
                                 FLAG_TNF_RFC2046, wifi_handover_type,
                                 os_strlen(wifi_handover_type), NULL, 0, buf);
 }
+
+
+struct wpabuf * ndef_build_wifi_hr(void)
+{
+       struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
+       struct wpabuf *carrier, *hc;
+
+       rn = wpabuf_alloc(2);
+       if (rn == NULL)
+               return NULL;
+       wpabuf_put_be16(rn, os_random() & 0xffff);
+
+       cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
+                              NULL, 0, rn);
+       wpabuf_free(rn);
+
+       if (cr == NULL)
+               return NULL;
+
+       ac_payload = wpabuf_alloc(4);
+       if (ac_payload == NULL) {
+               wpabuf_free(cr);
+               return NULL;
+       }
+       wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
+       wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
+       wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
+       wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
+
+       ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
+                              NULL, 0, ac_payload);
+       wpabuf_free(ac_payload);
+       if (ac == NULL) {
+               wpabuf_free(cr);
+               return NULL;
+       }
+
+       hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
+       if (hr_payload == NULL) {
+               wpabuf_free(cr);
+               wpabuf_free(ac);
+               return NULL;
+       }
+
+       wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
+       wpabuf_put_buf(hr_payload, cr);
+       wpabuf_put_buf(hr_payload, ac);
+       wpabuf_free(cr);
+       wpabuf_free(ac);
+
+       hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
+                              NULL, 0, hr_payload);
+       wpabuf_free(hr_payload);
+       if (hr == NULL)
+               return NULL;
+
+       carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
+       if (carrier == NULL) {
+               wpabuf_free(hr);
+               return NULL;
+       }
+       wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
+       wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
+       wpabuf_put_str(carrier, wifi_handover_type);
+
+       hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
+                              "0", 1, carrier);
+       wpabuf_free(carrier);
+       if (hc == NULL) {
+               wpabuf_free(hr);
+               return NULL;
+       }
+
+       return wpabuf_concat(hr, hc);
+}
index c45b68c..17ee620 100644 (file)
@@ -858,6 +858,7 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 /* ndef.c */
 struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
 struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi_hr(void);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
index 35be357..1ea9843 100644 (file)
@@ -351,3 +351,27 @@ an AP) using a special value "nfc-pw" in place of the PIN parameter. If
 the ER functionality has been started (wps_er_start), the NFC password
 token is used to enable enrollment of a new station (that was the source
 of the NFC password token).
+
+"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
+contents of a Handover Request Message for connection handover. The
+first argument selects the format of the output data and the second
+argument selects which type of connection handover is requested (WPS =
+Wi-Fi handover as specified in WSC 2.0).
+
+"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
+contents of a Handover Select Message for connection handover when this
+does not depend on the contents of the Handover Request Message. The
+first argument selects the format of the output data and the second
+argument selects which type of connection handover is requested (WPS =
+Wi-Fi handover as specified in WSC 2.0).
+
+"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt
+of NFC connection handover request. The payload may include multiple
+carriers the the applicable ones are matched based on the media
+type. The reply data is contents for the Handover Select Message
+(hexdump).
+
+"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt
+of NFC connection handover select. The payload may include multiple
+carriers the the applicable ones are matched based on the media
+type.
index 083543d..21b0e17 100644 (file)
@@ -846,6 +846,149 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
        return ret;
 }
 
+
+static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
+                                             char *reply, size_t max_len)
+{
+       struct wpabuf *buf;
+       int res;
+
+       buf = wpas_wps_nfc_handover_req(wpa_s);
+       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;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
+                                         char *cmd, char *reply,
+                                         size_t max_len)
+{
+       char *pos;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       if (os_strcmp(cmd, "NDEF") != 0)
+               return -1;
+
+       if (os_strcmp(pos, "WPS") == 0) {
+               return wpas_ctrl_nfc_get_handover_req_wps(wpa_s, reply,
+                                                         max_len);
+       }
+
+       return -1;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
+                                             char *reply, size_t max_len)
+{
+       struct wpabuf *buf;
+       int res;
+
+       buf = wpas_wps_nfc_handover_sel(wpa_s);
+       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;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
+                                         char *cmd, char *reply,
+                                         size_t max_len)
+{
+       char *pos;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       if (os_strcmp(cmd, "NDEF") != 0)
+               return -1;
+
+       if (os_strcmp(pos, "WPS") == 0) {
+               return wpas_ctrl_nfc_get_handover_sel_wps(wpa_s, reply,
+                                                         max_len);
+       }
+
+       return -1;
+}
+
+
+static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
+                                        char *cmd, char *reply,
+                                        size_t max_len)
+{
+       size_t len;
+       struct wpabuf *buf;
+       int ret;
+
+       len = os_strlen(cmd);
+       if (len & 0x01)
+               return -1;
+       len /= 2;
+
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return -1;
+       if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+               wpabuf_free(buf);
+               return -1;
+       }
+
+       ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf);
+       wpabuf_free(buf);
+
+       return ret;
+}
+
+
+static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+                                        char *cmd)
+{
+       size_t len;
+       struct wpabuf *buf;
+       int ret;
+
+       len = os_strlen(cmd);
+       if (len & 0x01)
+               return -1;
+       len /= 2;
+
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return -1;
+       if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+               wpabuf_free(buf);
+               return -1;
+       }
+
+       ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf);
+       wpabuf_free(buf);
+
+       return ret;
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 
@@ -4502,7 +4645,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        int reply_len;
 
        if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
-           os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+           os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
+           os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+           os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
                wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
                                      (const u8 *) buf, os_strlen(buf));
        } else {
@@ -4640,6 +4785,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
                                                               buf + 17))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
+               reply_len = wpas_ctrl_nfc_get_handover_req(
+                       wpa_s, buf + 21, reply, reply_size);
+       } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
+               reply_len = wpas_ctrl_nfc_get_handover_sel(
+                       wpa_s, buf + 21, reply, reply_size);
+       } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) {
+               reply_len = wpas_ctrl_nfc_rx_handover_req(
+                       wpa_s, buf + 20, reply, reply_size);
+       } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
+               if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
+                       reply_len = -1;
 #endif /* CONFIG_WPS_NFC */
        } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
                if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
index e510b9b..f7e6227 100644 (file)
@@ -796,6 +796,72 @@ static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
        return ret;
 }
 
+
+static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc,
+                                           char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
+                                           char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc,
+                                          char *argv[])
+{
+       int ret;
+       char *buf;
+       size_t buflen;
+
+       if (argc != 1) {
+               printf("Invalid 'nfc_rx_handover_req' command - one argument "
+                      "is required.\n");
+               return -1;
+       }
+
+       buflen = 21 + os_strlen(argv[0]);
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return -1;
+       os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]);
+
+       ret = wpa_ctrl_command(ctrl, buf);
+       os_free(buf);
+
+       return ret;
+}
+
+
+static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc,
+                                          char *argv[])
+{
+       int ret;
+       char *buf;
+       size_t buflen;
+
+       if (argc != 1) {
+               printf("Invalid 'nfc_rx_handover_sel' command - one argument "
+                      "is required.\n");
+               return -1;
+       }
+
+       buflen = 21 + os_strlen(argv[0]);
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return -1;
+       os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]);
+
+       ret = wpa_ctrl_command(ctrl, buf);
+       os_free(buf);
+
+       return ret;
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 
@@ -2436,6 +2502,18 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
          cli_cmd_flag_sensitive,
          "<hexdump of payload> = report read NFC tag with WPS data" },
+       { "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL,
+         cli_cmd_flag_none,
+         "<NDEF> <WPS> = create NFC handover request" },
+       { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
+         cli_cmd_flag_none,
+         "<NDEF> <WPS> = create NFC handover select" },
+       { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL,
+         cli_cmd_flag_none,
+         "<hexdump of payload> = report received NFC handover request" },
+       { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
+         cli_cmd_flag_none,
+         "<hexdump of payload> = report received NFC handover select" },
 #endif /* CONFIG_WPS_NFC */
        { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
          cli_cmd_flag_sensitive,
index dd48ab7..0655156 100644 (file)
@@ -1975,6 +1975,45 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
        return ret;
 }
 
+
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s)
+{
+       return ndef_build_wifi_hr();
+}
+
+
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s)
+{
+       return NULL;
+}
+
+
+int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
+                                const struct wpabuf *data)
+{
+       /* TODO */
+       return -1;
+}
+
+
+int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+                                const struct wpabuf *data)
+{
+       struct wpabuf *wps;
+       int ret;
+
+       wps = ndef_parse_wifi(data);
+       if (wps == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+                  "payload from NFC connection handover");
+       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
+       ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+       wpabuf_free(wps);
+
+       return ret;
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 
index 65c5c5a..2a3fea0 100644 (file)
@@ -69,6 +69,12 @@ 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);
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
                          const struct wpabuf *data);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s);
+int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
+                                const struct wpabuf *data);
+int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+                                const struct wpabuf *data);
 void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
                             struct wpa_scan_results *scan_res);
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);