WPS NFC: Validate peer public key hash on Enrollee
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 15 Feb 2013 19:29:22 +0000 (21:29 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 27 Jan 2014 19:10:55 +0000 (21:10 +0200)
Since the Enrollee can now get the public key hash from the Registrar,
there is need to validate this during the WPS protocol run.

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

src/eap_peer/eap_wsc.c
src/wps/wps.c
src/wps/wps.h
src/wps/wps_enrollee.c
src/wps/wps_i.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wps_supplicant.c
wpa_supplicant/wps_supplicant.h

index 8edb1ca..27ce2e3 100644 (file)
@@ -144,12 +144,13 @@ static void * eap_wsc_init(struct eap_sm *sm)
        size_t identity_len;
        int registrar;
        struct wps_config cfg;
-       const char *pos;
+       const char *pos, *end;
        const char *phase1;
        struct wps_context *wps;
        struct wps_credential new_ap_settings;
        int res;
        int nfc = 0;
+       u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
 
        wps = sm->wps;
        if (wps == NULL) {
@@ -220,6 +221,24 @@ static void * eap_wsc_init(struct eap_sm *sm)
        if (pos && cfg.pin)
                cfg.dev_pw_id = atoi(pos + 10);
 
+       pos = os_strstr(phase1, " pkhash=");
+       if (pos) {
+               size_t len;
+               pos += 8;
+               end = os_strchr(pos, ' ');
+               if (end)
+                       len = end - pos;
+               else
+                       len = os_strlen(pos);
+               if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
+                   hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
+                       wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
+                       os_free(data);
+                       return NULL;
+               }
+               cfg.peer_pubkey_hash = pkhash;
+       }
+
        res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
        if (res < 0) {
                os_free(data);
index 22d7eea..80343c8 100644 (file)
@@ -133,6 +133,12 @@ struct wps_data * wps_init(const struct wps_config *cfg)
        data->use_psk_key = cfg->use_psk_key;
        data->pbc_in_m1 = cfg->pbc_in_m1;
 
+       if (cfg->peer_pubkey_hash) {
+               os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+                         WPS_OOB_PUBKEY_HASH_LEN);
+               data->peer_pubkey_hash_set = 1;
+       }
+
        return data;
 }
 
index 15137a8..7e609c8 100644 (file)
@@ -183,6 +183,11 @@ struct wps_config {
         * PBC with the AP.
         */
        int pbc_in_m1;
+
+       /**
+        * peer_pubkey_hash - Peer public key hash or %NULL if not known
+        */
+       const u8 *peer_pubkey_hash;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
index 7b86ff7..0300582 100644 (file)
@@ -514,6 +514,23 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
                return -1;
        }
 
+       if (wps->peer_pubkey_hash_set) {
+               u8 hash[WPS_HASH_LEN];
+               sha256_vector(1, &pk, &pk_len, hash);
+               if (os_memcmp(hash, wps->peer_pubkey_hash,
+                             WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+                       wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+                       wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+                                   pk, pk_len);
+                       wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+                                   "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+                       wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+                                   wps->peer_pubkey_hash,
+                                   WPS_OOB_PUBKEY_HASH_LEN);
+                       return -1;
+               }
+       }
+
        wpabuf_free(wps->dh_pubkey_r);
        wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
        if (wps->dh_pubkey_r == NULL)
index aa8a6fa..22070db 100644 (file)
@@ -75,6 +75,9 @@ struct wps_data {
        size_t alt_dev_password_len;
        u16 alt_dev_pw_id;
 
+       u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+       int peer_pubkey_hash_set;
+
        /**
         * request_type - Request Type attribute from (Re)AssocReq
         */
index 68157d2..277e321 100644 (file)
@@ -813,7 +813,7 @@ static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
        else if (hwaddr_aton(cmd, bssid))
                return -1;
 
-       return wpas_wps_start_nfc(wpa_s, _bssid);
+       return wpas_wps_start_nfc(wpa_s, _bssid, NULL, 0, 0, NULL);
 }
 
 
index 038c7fa..c32457b 100644 (file)
@@ -1072,12 +1072,14 @@ 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 p2p_group, u16 dev_pw_id)
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                                const char *pin, int p2p_group, u16 dev_pw_id,
+                                const u8 *peer_pubkey_hash)
 {
        struct wpa_ssid *ssid;
-       char val[128];
+       char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
        unsigned int rpin = 0;
+       char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
 
        wpas_clear_wps(wpa_s);
        ssid = wpas_wps_add_network(wpa_s, 0, bssid);
@@ -1085,6 +1087,14 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
                return -1;
        ssid->temporary = 1;
        ssid->p2p_group = p2p_group;
+       if (peer_pubkey_hash) {
+               os_memcpy(hash, " pkhash=", 8);
+               wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+                                          peer_pubkey_hash,
+                                          WPS_OOB_PUBKEY_HASH_LEN);
+       } else {
+               hash[0] = '\0';
+       }
 #ifdef CONFIG_P2P
        if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
                ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1098,12 +1108,12 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
        }
 #endif /* CONFIG_P2P */
        if (pin)
-               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
-                           pin, dev_pw_id);
+               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+                           pin, dev_pw_id, hash);
        else {
                rpin = wps_generate_pin();
-               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
-                           rpin, dev_pw_id);
+               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+                           rpin, dev_pw_id, hash);
        }
        if (wpa_config_set(ssid, "phase1", val, 0) < 0)
                return -1;
@@ -1117,6 +1127,14 @@ int wpas_wps_start_pin(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 p2p_group, u16 dev_pw_id)
+{
+       return wpas_wps_start_dev_pw(wpa_s, bssid, pin, p2p_group, dev_pw_id,
+                                    NULL);
+}
+
+
 /* Cancel the wps pbc/pin requests */
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 {
@@ -2070,14 +2088,21 @@ 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_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const struct wpabuf *dev_pw, u16 dev_pw_id,
+                      int p2p_group, const u8 *peer_pubkey_hash)
 {
        struct wps_context *wps = wpa_s->wps;
        char pw[32 * 2 + 1];
 
+       if (dev_pw == NULL) {
+               dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+               dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+       }
+
        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)
+           dev_pw == NULL)
                return -1;
 
        dh5_free(wps->dh_ctx);
@@ -2103,10 +2128,9 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
        }
 
        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);
+                                  wpabuf_head(dev_pw), wpabuf_len(dev_pw));
+       return wpas_wps_start_dev_pw(wpa_s, bssid, pw, p2p_group, dev_pw_id,
+                                    peer_pubkey_hash);
 }
 
 
index 3fcfbbe..93cadda 100644 (file)
@@ -64,7 +64,9 @@ void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
 struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
                                          int ndef, const char *id_str);
 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_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const struct wpabuf *dev_pw, u16 dev_pw_id,
+                      int p2p_group, const u8 *peer_pubkey_hash);
 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, int cr);