DPP: Support multiple channels for initiating DPP Authentication

This extends wpa_supplicant to iterate over all available channels from
the intersection of what the peer indicates and the local device
supports when initiating DPP Authentication. In addition, retry DPP
Authentication Request frame up to five times if no response is
received.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2017-11-12 12:17:54 +02:00 committed by Jouni Malinen
parent de02986189
commit f97ace34cb
7 changed files with 307 additions and 48 deletions

View file

@ -503,8 +503,9 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
if (hapd->dpp_auth)
dpp_auth_deinit(hapd->dpp_auth);
/* TODO: hw_modes */
hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi,
configurator, 0);
configurator, 0, NULL, 0);
if (!hapd->dpp_auth)
goto fail;
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
@ -513,10 +514,6 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
/* TODO: Support iteration over all frequencies and filtering of
* frequencies based on locally enabled channels that allow initiation
* of transmission. */
if (peer_bi->num_freq > 0)
hapd->dpp_auth->curr_freq = peer_bi->freq[0];
else
hapd->dpp_auth->curr_freq = 2412;
if (is_zero_ether_addr(peer_bi->mac_addr)) {
dst = broadcast;

View file

@ -22,6 +22,7 @@
#include "crypto/aes_siv.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "drivers/driver.h"
#include "dpp.h"
@ -1701,11 +1702,171 @@ skip_wrapped_data:
}
static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
u16 num_modes, unsigned int freq)
{
u16 m;
int c, flag;
if (!own_modes || !num_modes)
return 1;
for (m = 0; m < num_modes; m++) {
for (c = 0; c < own_modes[m].num_channels; c++) {
if ((unsigned int) own_modes[m].channels[c].freq !=
freq)
continue;
flag = own_modes[m].channels[c].flag;
if (!(flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_NO_IR |
HOSTAPD_CHAN_RADAR)))
return 1;
}
}
wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
return 0;
}
static int freq_included(const unsigned int freqs[], unsigned int num,
unsigned int freq)
{
while (num > 0) {
if (freqs[--num] == freq)
return 1;
}
return 0;
}
static void freq_to_start(unsigned int freqs[], unsigned int num,
unsigned int freq)
{
unsigned int i;
for (i = 0; i < num; i++) {
if (freqs[i] == freq)
break;
}
if (i == 0 || i >= num)
return;
os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
freqs[0] = freq;
}
static int dpp_channel_intersect(struct dpp_authentication *auth,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
unsigned int i, freq;
for (i = 0; i < peer_bi->num_freq; i++) {
freq = peer_bi->freq[i];
if (freq_included(auth->freq, auth->num_freq, freq))
continue;
if (dpp_channel_ok_init(own_modes, num_modes, freq))
auth->freq[auth->num_freq++] = freq;
}
if (!auth->num_freq) {
wpa_printf(MSG_INFO,
"DPP: No available channels for initiating DPP Authentication");
return -1;
}
auth->curr_freq = auth->freq[0];
return 0;
}
static int dpp_channel_local_list(struct dpp_authentication *auth,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
u16 m;
int c, flag;
unsigned int freq;
auth->num_freq = 0;
if (!own_modes || !num_modes) {
auth->freq[0] = 2412;
auth->freq[1] = 2437;
auth->freq[2] = 2462;
auth->num_freq = 3;
return 0;
}
for (m = 0; m < num_modes; m++) {
for (c = 0; c < own_modes[m].num_channels; c++) {
freq = own_modes[m].channels[c].freq;
flag = own_modes[m].channels[c].flag;
if (flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_NO_IR |
HOSTAPD_CHAN_RADAR))
continue;
if (freq_included(auth->freq, auth->num_freq, freq))
continue;
auth->freq[auth->num_freq++] = freq;
if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
m = num_modes;
break;
}
}
}
return auth->num_freq == 0 ? -1 : 0;
}
static int dpp_prepare_channel_list(struct dpp_authentication *auth,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
int res;
char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
unsigned int i;
if (auth->peer_bi->num_freq > 0)
res = dpp_channel_intersect(auth, own_modes, num_modes);
else
res = dpp_channel_local_list(auth, own_modes, num_modes);
if (res < 0)
return res;
/* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
* likely channels first. */
freq_to_start(auth->freq, auth->num_freq, 2462);
freq_to_start(auth->freq, auth->num_freq, 2412);
freq_to_start(auth->freq, auth->num_freq, 2437);
auth->freq_idx = 0;
auth->curr_freq = auth->freq[0];
pos = freqs;
end = pos + sizeof(freqs);
for (i = 0; i < auth->num_freq; i++) {
res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
*pos = '\0';
wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
freqs);
return 0;
}
struct dpp_authentication * dpp_auth_init(void *msg_ctx,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
int configurator,
unsigned int neg_freq)
unsigned int neg_freq,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
struct dpp_authentication *auth;
size_t nonce_len;
@ -1720,11 +1881,15 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
return NULL;
auth->msg_ctx = msg_ctx;
auth->initiator = 1;
auth->waiting_auth_resp = 1;
auth->configurator = configurator;
auth->peer_bi = peer_bi;
auth->own_bi = own_bi;
auth->curve = peer_bi->curve;
if (dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
goto fail;
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->i_nonce, nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
@ -2910,6 +3075,8 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
u8 r_auth2[DPP_MAX_HASH_LEN];
u8 role;
auth->waiting_auth_resp = 0;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {

View file

@ -167,8 +167,13 @@ struct dpp_authentication {
EVP_PKEY *peer_protocol_key;
struct wpabuf *req_msg;
struct wpabuf *resp_msg;
/* Intersection of possible frequencies for initiating DPP
* Authentication exchange */
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
unsigned int num_freq, freq_idx;
unsigned int curr_freq;
unsigned int neg_freq;
unsigned int num_freq_iters;
size_t secret_len;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
@ -177,6 +182,7 @@ struct dpp_authentication {
u8 k2[DPP_MAX_HASH_LEN];
u8 ke[DPP_MAX_HASH_LEN];
int initiator;
int waiting_auth_resp;
int configurator;
int remove_on_tx_status;
int auth_success;
@ -298,11 +304,14 @@ int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri);
char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
const u8 *privkey, size_t privkey_len);
struct hostapd_hw_modes;
struct dpp_authentication * dpp_auth_init(void *msg_ctx,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
int configurator,
unsigned int neg_freq);
unsigned int neg_freq,
struct hostapd_hw_modes *own_modes,
u16 num_modes);
struct dpp_authentication *
dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
struct dpp_bootstrap_info *peer_bi,

View file

@ -153,6 +153,7 @@ extern "C" {
/* DPP events */
#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED "
#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "

View file

@ -605,6 +605,12 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
os_free(wpa_s->dpp_configurator_params);
wpa_s->dpp_configurator_params = os_strdup(value);
} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
wpa_s->dpp_init_max_tries = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
wpa_s->dpp_init_retry_time = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
wpa_s->dpp_resp_wait_time = atoi(value);
#endif /* CONFIG_DPP */
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
@ -7746,6 +7752,9 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
#ifdef CONFIG_DPP
wpas_dpp_deinit(wpa_s);
wpa_s->dpp_init_max_tries = 0;
wpa_s->dpp_init_retry_time = 0;
wpa_s->dpp_resp_wait_time = 0;
#endif /* CONFIG_DPP */
#ifdef CONFIG_TDLS

View file

@ -35,6 +35,8 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result result);
static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s);
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@ -305,6 +307,7 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
enum offchannel_send_action_result result)
{
const char *res_txt;
struct dpp_authentication *auth = wpa_s->dpp_auth;
res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
(result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
@ -323,6 +326,7 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
if (wpa_s->dpp_auth->remove_on_tx_status) {
wpa_printf(MSG_DEBUG,
"DPP: Terminate authentication exchange due to an earlier error");
eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
offchannel_send_action_done(wpa_s);
dpp_auth_deinit(wpa_s->dpp_auth);
@ -337,8 +341,13 @@ static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s,
result != OFFCHANNEL_SEND_ACTION_SUCCESS) {
wpa_printf(MSG_DEBUG,
"DPP: Unicast DPP Action frame was not ACKed");
/* TODO: In case of DPP Authentication Request frame, move to
* the next channel immediately */
if (auth->waiting_auth_resp) {
/* In case of DPP Authentication Request frame, move to
* the next channel immediately. */
offchannel_send_action_done(wpa_s);
wpas_dpp_auth_init_next(wpa_s);
return;
}
}
if (!wpa_s->dpp_auth_ok_on_ack && wpa_s->dpp_auth->neg_freq > 0 &&
@ -357,9 +366,25 @@ static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
unsigned int freq;
struct os_reltime now;
if (!wpa_s->dpp_auth)
return;
if (wpa_s->dpp_auth->waiting_auth_resp) {
unsigned int wait_time;
wait_time = wpa_s->dpp_resp_wait_time ?
wpa_s->dpp_resp_wait_time : 2;
os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->dpp_last_init,
wait_time)) {
offchannel_send_action_done(wpa_s);
wpas_dpp_auth_init_next(wpa_s);
return;
}
}
freq = wpa_s->dpp_auth->curr_freq;
if (wpa_s->dpp_auth->neg_freq > 0)
freq = wpa_s->dpp_auth->neg_freq;
@ -516,14 +541,90 @@ fail:
}
static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!wpa_s->dpp_auth)
return;
wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
wpas_dpp_auth_init_next(wpa_s);
}
static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s)
{
struct dpp_authentication *auth = wpa_s->dpp_auth;
const u8 *dst;
unsigned int wait_time, freq, max_tries;
if (!auth)
return -1;
if (auth->freq_idx >= auth->num_freq) {
auth->num_freq_iters++;
if (wpa_s->dpp_init_max_tries)
max_tries = wpa_s->dpp_init_max_tries;
else
max_tries = 5;
if (auth->num_freq_iters >= max_tries) {
wpa_printf(MSG_INFO,
"DPP: No response received from responder - stopping initiation attempt");
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout,
wpa_s, NULL);
offchannel_send_action_done(wpa_s);
dpp_auth_deinit(wpa_s->dpp_auth);
wpa_s->dpp_auth = NULL;
return -1;
}
auth->freq_idx = 0;
eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
if (wpa_s->dpp_init_retry_time)
wait_time = wpa_s->dpp_init_retry_time;
else
wait_time = 10000;
eloop_register_timeout(wait_time / 1000,
(wait_time % 1000) * 1000,
wpas_dpp_init_timeout, wpa_s,
NULL);
return 0;
}
freq = auth->freq[auth->freq_idx++];
auth->curr_freq = freq;
if (is_zero_ether_addr(auth->peer_bi->mac_addr))
dst = broadcast;
else
dst = auth->peer_bi->mac_addr;
wpa_s->dpp_auth_ok_on_ack = 0;
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
wpas_dpp_reply_wait_timeout,
wpa_s, NULL);
if (auth->neg_freq > 0 && freq != auth->neg_freq) {
wpa_printf(MSG_DEBUG,
"DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
freq, auth->neg_freq);
}
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
os_get_reltime(&wpa_s->dpp_last_init);
return offchannel_send_action(wpa_s, freq, dst,
wpa_s->own_addr, broadcast,
wpabuf_head(auth->req_msg),
wpabuf_len(auth->req_msg),
wait_time, wpas_dpp_tx_status, 0);
}
int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd)
{
const char *pos;
struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
const u8 *dst;
int res;
int configurator = 1;
unsigned int wait_time;
unsigned int neg_freq = 0;
wpa_s->dpp_gas_client = 0;
@ -579,57 +680,26 @@ int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd)
neg_freq = atoi(pos + 10);
if (wpa_s->dpp_auth) {
eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
offchannel_send_action_done(wpa_s);
dpp_auth_deinit(wpa_s->dpp_auth);
}
wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator,
neg_freq);
neg_freq,
wpa_s->hw.modes, wpa_s->hw.num_modes);
if (!wpa_s->dpp_auth)
goto fail;
wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth);
wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd);
/* TODO: Support iteration over all frequencies and filtering of
* frequencies based on locally enabled channels that allow initiation
* of transmission. */
if (peer_bi->num_freq > 0)
wpa_s->dpp_auth->curr_freq = peer_bi->freq[0];
else
wpa_s->dpp_auth->curr_freq = 2412;
wpa_s->dpp_auth->neg_freq = neg_freq;
if (is_zero_ether_addr(peer_bi->mac_addr)) {
dst = broadcast;
} else {
dst = peer_bi->mac_addr;
if (!is_zero_ether_addr(peer_bi->mac_addr))
os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr,
ETH_ALEN);
}
wpa_s->dpp_auth_ok_on_ack = 0;
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
wpas_dpp_reply_wait_timeout,
wpa_s, NULL);
if (neg_freq > 0 && wpa_s->dpp_auth->curr_freq != neg_freq) {
wpa_printf(MSG_DEBUG,
"DPP: Initiate on curr_freq %u MHz and move to neg_freq %u MHz for response",
wpa_s->dpp_auth->curr_freq,
wpa_s->dpp_auth->neg_freq);
}
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
MAC2STR(dst), wpa_s->dpp_auth->curr_freq,
DPP_PA_AUTHENTICATION_REQ);
res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq,
dst, wpa_s->own_addr, broadcast,
wpabuf_head(wpa_s->dpp_auth->req_msg),
wpabuf_len(wpa_s->dpp_auth->req_msg),
wait_time, wpas_dpp_tx_status, 0);
return res;
return wpas_dpp_auth_init_next(wpa_s);
fail:
return -1;
}
@ -1228,6 +1298,7 @@ static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
auth->curr_freq = freq;
}
eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
msg = dpp_auth_resp_rx(auth, hdr, buf, len);
if (!msg) {
if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
@ -2162,6 +2233,7 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
if (!wpa_s->dpp_init_done)
return;
eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL);
offchannel_send_action_done(wpa_s);
wpas_dpp_listen_stop(wpa_s);
dpp_bootstrap_del(wpa_s, 0);

View file

@ -1199,6 +1199,10 @@ struct wpa_supplicant {
char *dpp_pkex_identifier;
char *dpp_pkex_auth_cmd;
char *dpp_configurator_params;
struct os_reltime dpp_last_init;
unsigned int dpp_init_max_tries;
unsigned int dpp_init_retry_time;
unsigned int dpp_resp_wait_time;
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;