P2P NFC: Report connection handover as trigger for P2P
authorJouni Malinen <jouni@qca.qualcomm.com>
Sat, 27 Apr 2013 15:50:14 +0000 (18:50 +0300)
committerJouni Malinen <j@w1.fi>
Mon, 27 Jan 2014 19:10:56 +0000 (21:10 +0200)
"NFC_REPORT_HANDOVER {INIT,RESP} P2P <req> <sel>" can now be used to
report completed NFC negotiated connection handover in which the P2P
alternative carrier was selected.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

12 files changed:
src/p2p/p2p.c
src/p2p/p2p.h
src/p2p/p2p_go_neg.c
src/p2p/p2p_i.h
src/wps/wps.c
src/wps/wps_registrar.c
wpa_supplicant/ap.c
wpa_supplicant/ap.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/p2p_supplicant.h
wpa_supplicant/wpa_supplicant_i.h

index ae3ba85..67bd859 100644 (file)
@@ -4421,4 +4421,106 @@ struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p)
        return p2p_build_nfc_handover(p2p);
 }
 
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+                                       struct p2p_nfc_params *params)
+{
+       struct p2p_message msg;
+       struct p2p_device *dev;
+       const u8 *p2p_dev_addr;
+       int peer_go = 0;
+
+       params->next_step = NO_ACTION;
+
+       if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+                                  params->p2p_attr, params->p2p_len, &msg)) {
+               p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       if (msg.p2p_device_addr)
+               p2p_dev_addr = msg.p2p_device_addr;
+       else if (msg.device_id)
+               p2p_dev_addr = msg.device_id;
+       else {
+               p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       if (msg.oob_dev_password) {
+               os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+                         msg.oob_dev_password_len);
+               params->oob_dev_pw_len = msg.oob_dev_password_len;
+       }
+
+       dev = p2p_create_device(p2p, p2p_dev_addr);
+       if (dev == NULL) {
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       params->peer = &dev->info;
+
+       os_get_reltime(&dev->last_seen);
+       dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+       p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+       if (msg.oob_go_neg_channel) {
+               int freq;
+               if (msg.oob_go_neg_channel[3] == 0 &&
+                   msg.oob_go_neg_channel[4] == 0)
+                       freq = 0;
+               else
+                       freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+                                                  msg.oob_go_neg_channel[4]);
+               if (freq < 0) {
+                       p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+               } else {
+                       p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+                       dev->oob_go_neg_freq = freq;
+               }
+
+               if (!params->sel) {
+                       freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+                                                  p2p->cfg->channel);
+                       if (freq < 0) {
+                               p2p_dbg(p2p, "Own listen channel not known");
+                               return -1;
+                       }
+                       p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz",
+                               freq);
+                       dev->oob_go_neg_freq = freq;
+               }
+
+               if (msg.oob_go_neg_channel[5] == P2P_GO_IN_A_GROUP)
+                       peer_go = 1;
+       }
+
+       p2p_parse_free(&msg);
+
+       if (dev->flags & P2P_DEV_USER_REJECTED) {
+               p2p_dbg(p2p, "Do not report rejected device");
+               return 0;
+       }
+
+       if (!(dev->flags & P2P_DEV_REPORTED)) {
+               p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+                                   !(dev->flags & P2P_DEV_REPORTED_ONCE));
+               dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+       }
+
+       if (peer_go)
+               params->next_step = JOIN_GROUP;
+       else if (p2p->num_groups > 0)
+               params->next_step = AUTH_JOIN;
+       else if (params->sel)
+               params->next_step = INIT_GO_NEG;
+       else
+               params->next_step = RESP_GO_NEG;
+
+       return 0;
+}
+
 #endif /* CONFIG_WPS_NFC */
index 1dc71fa..2a64ca5 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef P2P_H
 #define P2P_H
 
+#include "wps/wps_defs.h"
+
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
@@ -1900,4 +1902,23 @@ const char * p2p_get_state_txt(struct p2p_data *p2p);
 struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p);
 struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p);
 
+struct p2p_nfc_params {
+       int sel;
+       const u8 *wsc_attr;
+       size_t wsc_len;
+       const u8 *p2p_attr;
+       size_t p2p_len;
+
+       enum {
+               NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG
+       } next_step;
+       struct p2p_peer_info *peer;
+       u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                     WPS_OOB_DEVICE_PASSWORD_LEN];
+       size_t oob_dev_pw_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+                                       struct p2p_nfc_params *params);
+
 #endif /* P2P_H */
index f23cff6..76436f5 100644 (file)
@@ -217,6 +217,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
        }
 
        freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (dev->oob_go_neg_freq > 0)
+               freq = dev->oob_go_neg_freq;
        if (freq <= 0) {
                p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
                        MACSTR " to send GO Negotiation Request",
@@ -614,7 +616,11 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
        if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
                p2p_dbg(p2p, "User has rejected this peer");
                status = P2P_SC_FAIL_REJECTED_BY_USER;
-       } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+       } else if (dev == NULL ||
+                  (dev->wps_method == WPS_NOT_READY &&
+                   (p2p->authorized_oob_dev_pw_id == 0 ||
+                    p2p->authorized_oob_dev_pw_id !=
+                    msg.dev_password_id))) {
                p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
                        MAC2STR(sa));
                status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -701,6 +707,28 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
                        }
                        break;
                default:
+                       if (msg.dev_password_id &&
+                           msg.dev_password_id == dev->oob_pw_id) {
+                               p2p_dbg(p2p, "Peer using NFC");
+                               if (dev->wps_method != WPS_NFC) {
+                                       p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                                               p2p_wps_method_str(
+                                                       dev->wps_method));
+                                       status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                                       goto fail;
+                               }
+                               break;
+                       }
+#ifdef CONFIG_WPS_NFC
+                       if (p2p->authorized_oob_dev_pw_id &&
+                           msg.dev_password_id ==
+                           p2p->authorized_oob_dev_pw_id) {
+                               p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+                               dev->wps_method = WPS_NFC;
+                               dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+                               break;
+                       }
+#endif /* CONFIG_WPS_NFC */
                        p2p_dbg(p2p, "Unsupported Device Password ID %d",
                                msg.dev_password_id);
                        status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1026,6 +1054,17 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
                }
                break;
        default:
+               if (msg.dev_password_id &&
+                   msg.dev_password_id == dev->oob_pw_id) {
+                       p2p_dbg(p2p, "Peer using NFC");
+                       if (dev->wps_method != WPS_NFC) {
+                               p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                                       p2p_wps_method_str(dev->wps_method));
+                               status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                               goto fail;
+                       }
+                       break;
+               }
                p2p_dbg(p2p, "Unsupported Device Password ID %d",
                        msg.dev_password_id);
                status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
index 2101841..1150d40 100644 (file)
@@ -27,6 +27,7 @@ struct p2p_device {
        struct dl_list list;
        struct os_reltime last_seen;
        int listen_freq;
+       int oob_go_neg_freq;
        enum p2p_wps_method wps_method;
        u16 oob_pw_id;
 
@@ -465,6 +466,8 @@ struct p2p_data {
        struct wpabuf *wfd_assoc_bssid;
        struct wpabuf *wfd_coupled_sink_info;
 #endif /* CONFIG_WIFI_DISPLAY */
+
+       u16 authorized_oob_dev_pw_id;
 };
 
 /**
index a38a6db..b40a68a 100644 (file)
@@ -178,7 +178,6 @@ void wps_deinit(struct wps_data *data)
        wps_device_data_free(&data->peer_dev);
        os_free(data->new_ap_settings);
        dh5_free(data->dh_ctx);
-       os_free(data->nfc_pw_token);
        os_free(data);
 }
 
index f8f2ae1..56b8e23 100644 (file)
@@ -31,6 +31,7 @@
 struct wps_nfc_pw_token {
        struct dl_list list;
        u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+       unsigned int peer_pk_hash_known:1;
        u16 pw_id;
        u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
        size_t dev_pw_len;
@@ -1822,7 +1823,8 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps)
        }
 
 #ifdef CONFIG_WPS_NFC
-       if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob) {
+       if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+           wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
                /*
                 * Use abbreviated handshake since public key hash allowed
                 * Enrollee to validate our public key similarly to how Enrollee
@@ -2585,7 +2587,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
                           wps->dev_pw_id, wps->wps, wps->wps->registrar);
                token = wps_get_nfc_pw_token(
                        &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
-               if (token) {
+               if (token && token->peer_pk_hash_known) {
                        wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
                                   "Password Token");
                        dl_list_del(&token->list);
@@ -2602,6 +2604,10 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
                                        WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
                                return WPS_CONTINUE;
                        }
+               } else if (token) {
+                       wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+                                  "Password Token (no peer PK hash)");
+                       wps->nfc_pw_token = token;
                }
        }
 #endif /* CONFIG_WPS_NFC */
@@ -3543,13 +3549,23 @@ int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
        if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
                return -1;
 
+       if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+           (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+                          "addition - missing public key hash");
+               return -1;
+       }
+
        wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
 
        token = os_zalloc(sizeof(*token));
        if (token == NULL)
                return -1;
 
-       os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+       token->peer_pk_hash_known = pubkey_hash != NULL;
+       if (pubkey_hash)
+               os_memcpy(token->pubkey_hash, pubkey_hash,
+                         WPS_OOB_PUBKEY_HASH_LEN);
        token->pw_id = pw_id;
        token->pk_hash_provided_oob = pk_hash_provided_oob;
        if (dev_pw) {
@@ -3615,6 +3631,14 @@ void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
        wps_registrar_remove_authorized_mac(reg,
                                            (u8 *) "\xff\xff\xff\xff\xff\xff");
        wps_registrar_selected_registrar_changed(reg, 0);
+
+       /*
+        * Free the NFC password token if it was used only for a single protocol
+        * run. The static handover case uses the same password token multiple
+        * times, so do not free that case here.
+        */
+       if (token->peer_pk_hash_known)
+               os_free(token);
 }
 
 #endif /* CONFIG_WPS_NFC */
index 0f80249..9a09e3e 100644 (file)
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "eapol_supp/eapol_supp_sm.h"
+#include "crypto/dh_group5.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ap_drv_ops.h"
@@ -1152,3 +1153,48 @@ int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
 
        return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+                          const struct wpabuf *pw, const u8 *pubkey_hash)
+{
+       struct hostapd_data *hapd;
+       struct wps_context *wps;
+
+       if (!wpa_s->ap_iface)
+               return -1;
+       hapd = wpa_s->ap_iface->bss[0];
+       wps = hapd->wps;
+
+       if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL ||
+           wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
+               return -1;
+       }
+
+       dh5_free(wps->dh_ctx);
+       wpabuf_free(wps->dh_pubkey);
+       wpabuf_free(wps->dh_privkey);
+       wps->dh_privkey = wpabuf_dup(
+               wpa_s->parent->conf->wps_nfc_dh_privkey);
+       wps->dh_pubkey = wpabuf_dup(
+               wpa_s->parent->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;
+
+       return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash,
+                                             pw_id,
+                                             pw ? wpabuf_head(pw) : NULL,
+                                             pw ? wpabuf_len(pw) : 0, 1);
+}
+#endif /* CONFIG_WPS_NFC */
index 9138717..8aa5ffa 100644 (file)
@@ -72,5 +72,7 @@ wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
                                    const struct wpabuf *req,
                                    const struct wpabuf *sel);
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+                          const struct wpabuf *pw, const u8 *pubkey_hash);
 
 #endif /* AP_H */
index cde9c46..3bfcc90 100644 (file)
@@ -1193,6 +1193,12 @@ static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
                ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
                if (ret < 0)
                        ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+       } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+       {
+               ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel);
+       } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+       {
+               ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel);
        } else {
                wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
                           "reported: role=%s type=%s", role, type);
index e61d5ce..4e9dcc3 100644 (file)
@@ -1107,9 +1107,20 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
                          res->ssid, res->ssid_len);
        wpa_supplicant_ap_deinit(wpa_s);
        wpas_copy_go_neg_results(wpa_s, res);
-       if (res->wps_method == WPS_PBC)
+       if (res->wps_method == WPS_PBC) {
                wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
-       else {
+#ifdef CONFIG_WPS_NFC
+       } else if (res->wps_method == WPS_NFC) {
+               wpas_wps_start_nfc(wpa_s, res->peer_interface_addr,
+                                  wpa_s->parent->p2p_oob_dev_pw,
+                                  wpa_s->parent->p2p_oob_dev_pw_id, 1,
+                                  wpa_s->parent->p2p_oob_dev_pw_id ==
+                                  DEV_PW_NFC_CONNECTION_HANDOVER ?
+                                  wpa_s->parent->p2p_peer_oob_pubkey_hash :
+                                  NULL,
+                                  NULL, 0);
+#endif /* CONFIG_WPS_NFC */
+       } else {
                u16 dev_pw_id = DEV_PW_DEFAULT;
                if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
                        dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
@@ -1233,10 +1244,24 @@ static void p2p_go_configured(void *ctx, void *data)
                           "filtering");
                return;
        }
-       if (params->wps_method == WPS_PBC)
+       if (params->wps_method == WPS_PBC) {
                wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
                                          params->peer_device_addr);
-       else if (wpa_s->p2p_pin[0])
+#ifdef CONFIG_WPS_NFC
+       } else if (params->wps_method == WPS_NFC) {
+               if (wpa_s->parent->p2p_oob_dev_pw_id !=
+                   DEV_PW_NFC_CONNECTION_HANDOVER &&
+                   !wpa_s->parent->p2p_oob_dev_pw) {
+                       wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+                       return;
+               }
+               wpas_ap_wps_add_nfc_pw(
+                       wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+                       wpa_s->parent->p2p_oob_dev_pw,
+                       wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+                       wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+#endif /* CONFIG_WPS_NFC */
+       } else if (wpa_s->p2p_pin[0])
                wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
                                          wpa_s->p2p_pin, NULL, 0, 0);
        os_free(wpa_s->go_params);
@@ -3848,6 +3873,9 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
        }
        eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
 
+       wpabuf_free(wpa_s->p2p_oob_dev_pw);
+       wpa_s->p2p_oob_dev_pw = NULL;
+
        /* TODO: remove group interface from the driver if this wpa_s instance
         * is on top of a P2P group interface */
 }
@@ -3942,7 +3970,9 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
                           go_intent, own_interface_addr, force_freq,
                           persistent_group, ssid ? ssid->ssid : NULL,
                           ssid ? ssid->ssid_len : 0,
-                          wpa_s->p2p_pd_before_go_neg, pref_freq, 0);
+                          wpa_s->p2p_pd_before_go_neg, pref_freq,
+                          wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+                          0);
 }
 
 
@@ -3959,7 +3989,9 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
        return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
                             go_intent, own_interface_addr, force_freq,
                             persistent_group, ssid ? ssid->ssid : NULL,
-                            ssid ? ssid->ssid_len : 0, pref_freq, 0);
+                            ssid ? ssid->ssid_len : 0, pref_freq,
+                            wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+                            0);
 }
 
 
@@ -7025,4 +7057,240 @@ struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
        return wpas_p2p_nfc_handover(ndef, wsc, p2p);
 }
 
+
+static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
+                                  struct p2p_nfc_params *params)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
+                  "connection handover");
+       return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+                               WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
+                               0, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
+                                 struct p2p_nfc_params *params)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
+                  "connection handover");
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               struct wpa_ssid *ssid = wpa_s->current_ssid;
+               if (ssid == NULL)
+                       continue;
+               if (ssid->mode != WPAS_MODE_P2P_GO)
+                       continue;
+               if (wpa_s->ap_iface == NULL)
+                       continue;
+               break;
+       }
+       if (wpa_s == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
+               return -1;
+       }
+
+       if (wpa_s->parent->p2p_oob_dev_pw_id !=
+           DEV_PW_NFC_CONNECTION_HANDOVER &&
+           !wpa_s->parent->p2p_oob_dev_pw) {
+               wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+               return -1;
+       }
+       return wpas_ap_wps_add_nfc_pw(
+               wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+               wpa_s->parent->p2p_oob_dev_pw,
+               wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+               wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+}
+
+
+static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
+                                   struct p2p_nfc_params *params)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
+                  "connection handover");
+       return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+                               WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
+                               0, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
+                                   struct p2p_nfc_params *params)
+{
+       int res;
+
+       wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
+                  "connection handover");
+       res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+                              WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
+                              0, -1, 0, 1, 1);
+       if (res)
+               return res;
+
+       res = wpas_p2p_listen(wpa_s, 60);
+       if (res) {
+               p2p_unauthorize(wpa_s->global->p2p,
+                               params->peer->p2p_device_addr);
+       }
+
+       return res;
+}
+
+
+static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
+                                           const struct wpabuf *data,
+                                           int sel, int tag)
+{
+       const u8 *pos, *end;
+       u16 len, id;
+       struct p2p_nfc_params params;
+       int res;
+
+       os_memset(&params, 0, sizeof(params));
+       params.sel = sel;
+
+       wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
+
+       pos = wpabuf_head(data);
+       end = pos + wpabuf_len(data);
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
+                          "attributes");
+               return -1;
+       }
+       len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (pos + len > end) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
+                          "attributes");
+               return -1;
+       }
+       params.wsc_attr = pos;
+       params.wsc_len = len;
+       pos += len;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
+                          "attributes");
+               return -1;
+       }
+       len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (pos + len > end) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
+                          "attributes");
+               return -1;
+       }
+       params.p2p_attr = pos;
+       params.p2p_len = len;
+       pos += len;
+
+       wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
+                   params.wsc_attr, params.wsc_len);
+       wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
+                   params.p2p_attr, params.p2p_len);
+       if (pos < end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "P2P: Ignored extra data after P2P attributes",
+                           pos, end - pos);
+       }
+
+       res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, &params);
+       if (res)
+               return res;
+
+       wpabuf_free(wpa_s->p2p_oob_dev_pw);
+       wpa_s->p2p_oob_dev_pw = NULL;
+
+       if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
+                          "received");
+               return -1;
+       }
+
+       id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
+       wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
+       wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
+                   params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+       os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
+                 params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+       wpa_s->p2p_peer_oob_pk_hash_known = 1;
+
+       if (tag) {
+               if (id < 0x10) {
+                       wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
+                                  "peer OOB Device Password Id %u", id);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
+                          "Device Password Id %u", id);
+               wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
+                               params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+                               params.oob_dev_pw_len -
+                               WPS_OOB_PUBKEY_HASH_LEN - 2);
+               wpa_s->p2p_oob_dev_pw_id = id;
+               wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
+                       params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+                       params.oob_dev_pw_len -
+                       WPS_OOB_PUBKEY_HASH_LEN - 2);
+               if (wpa_s->p2p_oob_dev_pw == NULL)
+                       return -1;
+
+               if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+                   wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                                  &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+                       return -1;
+       } else {
+               wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
+                          "without Device Password");
+               wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+       }
+
+       switch (params.next_step) {
+       case NO_ACTION:
+               return 0;
+       case JOIN_GROUP:
+               return wpas_p2p_nfc_join_group(wpa_s, &params);
+       case AUTH_JOIN:
+               return wpas_p2p_nfc_auth_join(wpa_s, &params);
+       case INIT_GO_NEG:
+               return wpas_p2p_nfc_init_go_neg(wpa_s, &params);
+       case RESP_GO_NEG:
+               /* TODO: use own OOB Dev Pw */
+               return wpas_p2p_nfc_resp_go_neg(wpa_s, &params);
+       }
+
+       return -1;
+}
+
+
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+                                const struct wpabuf *req,
+                                const struct wpabuf *sel)
+{
+       struct wpabuf *tmp;
+       int ret;
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
+
+       wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
+                         wpabuf_head(req), wpabuf_len(req));
+       wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
+                         wpabuf_head(sel), wpabuf_len(sel));
+       tmp = ndef_parse_p2p(init ? sel : req);
+       if (tmp == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
+               return -1;
+       }
+
+       ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0);
+       wpabuf_free(tmp);
+
+       return ret;
+}
+
 #endif /* CONFIG_WPS_NFC */
index 17e7bae..0f09d17 100644 (file)
@@ -152,6 +152,9 @@ struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
                                          int ndef);
 struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
                                          int ndef, int tag);
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+                                const struct wpabuf *req,
+                                const struct wpabuf *sel);
 
 #ifdef CONFIG_P2P
 int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
index 68995fc..df07f46 100644 (file)
@@ -12,6 +12,7 @@
 #include "utils/list.h"
 #include "common/defs.h"
 #include "common/sae.h"
+#include "wps/wps_defs.h"
 #include "config_ssid.h"
 
 extern const char *wpa_supplicant_version;
@@ -713,6 +714,7 @@ struct wpa_supplicant {
        unsigned int p2p_go_group_formation_completed:1;
        unsigned int waiting_presence_resp;
        int p2p_first_connection_timeout;
+       unsigned int p2p_peer_oob_pk_hash_known:1;
        int p2p_persistent_go_freq;
        int p2p_persistent_id;
        int p2p_go_intent;
@@ -722,6 +724,11 @@ struct wpa_supplicant {
        struct wpa_radio_work *p2p_scan_work;
        struct wpa_radio_work *p2p_listen_work;
        struct wpa_radio_work *p2p_send_action_work;
+
+       u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
+       struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
+                                       * formation */
+       u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
 #endif /* CONFIG_P2P */
 
        struct wpa_ssid *bgscan_ssid;