DPP: PKEX bootstrapping

This implements genric PKEX functionality in src/common/dpp.c and glue
code to use this in wpa_supplicant (i.e, hostapd DPP implementation does
not yet support PKEX).

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2017-07-02 12:36:23 +03:00 committed by Jouni Malinen
parent b9d47b4848
commit 500ed7f006
7 changed files with 1862 additions and 7 deletions

File diff suppressed because it is too large Load diff

View file

@ -81,10 +81,12 @@ struct dpp_curve_params {
size_t nonce_len;
size_t prime_len;
const char *jwk_crv;
u16 ike_group;
};
enum dpp_bootstrap_type {
DPP_BOOTSTRAP_QR_CODE,
DPP_BOOTSTRAP_PKEX,
};
struct dpp_bootstrap_info {
@ -102,6 +104,24 @@ struct dpp_bootstrap_info {
const struct dpp_curve_params *curve;
};
struct dpp_pkex {
unsigned int initiator:1;
unsigned int exchange_done:1;
struct dpp_bootstrap_info *own_bi;
u8 own_mac[ETH_ALEN];
u8 peer_mac[ETH_ALEN];
char *identifier;
char *code;
EVP_PKEY *x;
EVP_PKEY *y;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
u8 z[DPP_MAX_HASH_LEN];
EVP_PKEY *peer_bootstrap_key;
struct wpabuf *exchange_req;
struct wpabuf *exchange_resp;
};
struct dpp_configuration {
u8 ssid[32];
size_t ssid_len;
@ -189,6 +209,7 @@ struct dpp_introduction {
};
void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
const char *chan_list);
int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
@ -235,5 +256,22 @@ int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len);
struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier,
const char *code);
struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const u8 *peer_mac,
const char *identifier,
const char *code,
const u8 *buf, size_t len);
struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
const u8 *buf, size_t len);
struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
const u8 *buf, size_t len);
int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex,
const u8 *buf, size_t len);
void dpp_pkex_free(struct dpp_pkex *pkex);
#endif /* DPP_H */

View file

@ -10251,6 +10251,20 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) {
if (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
int res;
res = wpas_dpp_pkex_add(wpa_s, buf + 12);
if (res < 0) {
reply_len = -1;
} else {
reply_len = os_snprintf(reply, reply_size, "%d", res);
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0)
reply_len = -1;
#endif /* CONFIG_DPP */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);

View file

@ -151,6 +151,8 @@ int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd)
if (os_strstr(cmd, "type=qrcode"))
bi->type = DPP_BOOTSTRAP_QR_CODE;
else if (os_strstr(cmd, "type=pkex"))
bi->type = DPP_BOOTSTRAP_PKEX;
else
goto fail;
@ -281,6 +283,8 @@ static const char * wpas_dpp_bootstrap_type(enum dpp_bootstrap_type type)
switch (type) {
case DPP_BOOTSTRAP_QR_CODE:
return "QRCODE";
case DPP_BOOTSTRAP_PKEX:
return "PKEX";
}
return "??";
}
@ -1339,6 +1343,226 @@ fail:
}
static void
wpas_dpp_tx_pkex_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result result)
{
wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
" result=%s (PKEX)",
freq, MAC2STR(dst),
result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
(result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
"FAILED"));
/* TODO: Time out wait for response more quickly in error cases? */
}
static void
wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
struct wpabuf *msg;
unsigned int wait_time;
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
MAC2STR(src));
/* TODO: Support multiple PKEX codes by iterating over all the enabled
* values here */
if (!wpa_s->dpp_pkex_code || !wpa_s->dpp_pkex_bi) {
wpa_printf(MSG_DEBUG,
"DPP: No PKEX code configured - ignore request");
return;
}
if (wpa_s->dpp_pkex) {
/* TODO: Support parallel operations */
wpa_printf(MSG_DEBUG,
"DPP: Already in PKEX session - ignore new request");
return;
}
wpa_s->dpp_pkex = dpp_pkex_rx_exchange_req(wpa_s->dpp_pkex_bi,
wpa_s->own_addr, src,
wpa_s->dpp_pkex_identifier,
wpa_s->dpp_pkex_code,
buf, len);
if (!wpa_s->dpp_pkex) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to process the request - ignore it");
return;
}
msg = wpa_s->dpp_pkex->exchange_resp;
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
broadcast,
wpabuf_head(msg), wpabuf_len(msg),
wait_time, wpas_dpp_tx_pkex_status, 0);
}
static void
wpas_dpp_rx_pkex_exchange_resp(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
struct wpabuf *msg;
unsigned int wait_time;
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
MAC2STR(src));
/* TODO: Support multiple PKEX codes by iterating over all the enabled
* values here */
if (!wpa_s->dpp_pkex || !wpa_s->dpp_pkex->initiator ||
wpa_s->dpp_pkex->exchange_done) {
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
return;
}
os_memcpy(wpa_s->dpp_pkex->peer_mac, src, ETH_ALEN);
msg = dpp_pkex_rx_exchange_resp(wpa_s->dpp_pkex, buf, len);
if (!msg) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
return;
}
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
MAC2STR(src));
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
broadcast,
wpabuf_head(msg), wpabuf_len(msg),
wait_time, wpas_dpp_tx_pkex_status, 0);
wpabuf_free(msg);
}
static void
wpas_dpp_rx_pkex_commit_reveal_req(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
struct wpabuf *msg;
unsigned int wait_time;
struct dpp_pkex *pkex = wpa_s->dpp_pkex;
struct dpp_bootstrap_info *bi;
wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
MAC2STR(src));
if (!pkex || pkex->initiator || !pkex->exchange_done) {
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
return;
}
msg = dpp_pkex_rx_commit_reveal_req(pkex, buf, len);
if (!msg) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
return;
}
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
MACSTR, MAC2STR(src));
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr,
broadcast,
wpabuf_head(msg), wpabuf_len(msg),
wait_time, wpas_dpp_tx_pkex_status, 0);
wpabuf_free(msg);
bi = os_zalloc(sizeof(*bi));
if (!bi)
return;
bi->id = wpas_dpp_next_id(wpa_s);
bi->type = DPP_BOOTSTRAP_PKEX;
os_memcpy(bi->mac_addr, src, ETH_ALEN);
bi->num_freq = 1;
bi->freq[0] = freq;
bi->curve = pkex->own_bi->curve;
bi->pubkey = pkex->peer_bootstrap_key;
pkex->peer_bootstrap_key = NULL;
dpp_pkex_free(pkex);
wpa_s->dpp_pkex = NULL;
if (dpp_bootstrap_key_hash(bi) < 0) {
dpp_bootstrap_info_free(bi);
return;
}
dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
}
static void
wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *buf, size_t len,
unsigned int freq)
{
int res;
struct dpp_bootstrap_info *bi, *own_bi;
struct dpp_pkex *pkex = wpa_s->dpp_pkex;
char cmd[500];
wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
MAC2STR(src));
if (!pkex || !pkex->initiator || !pkex->exchange_done) {
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
return;
}
res = dpp_pkex_rx_commit_reveal_resp(pkex, buf, len);
if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
return;
}
own_bi = pkex->own_bi;
bi = os_zalloc(sizeof(*bi));
if (!bi)
return;
bi->id = wpas_dpp_next_id(wpa_s);
bi->type = DPP_BOOTSTRAP_PKEX;
os_memcpy(bi->mac_addr, src, ETH_ALEN);
bi->num_freq = 1;
bi->freq[0] = freq;
bi->curve = own_bi->curve;
bi->pubkey = pkex->peer_bootstrap_key;
pkex->peer_bootstrap_key = NULL;
dpp_pkex_free(pkex);
wpa_s->dpp_pkex = NULL;
if (dpp_bootstrap_key_hash(bi) < 0) {
dpp_bootstrap_info_free(bi);
return;
}
dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
bi->id,
wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : "");
wpa_printf(MSG_DEBUG,
"DPP: Start authentication after PKEX with parameters: %s",
cmd);
if (wpas_dpp_auth_init(wpa_s, cmd) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Authentication initialization failed");
return;
}
}
void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
@ -1371,6 +1595,18 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
case DPP_PA_PEER_DISCOVERY_RESP:
wpas_dpp_rx_peer_disc_resp(wpa_s, src, buf, len);
break;
case DPP_PA_PKEX_EXCHANGE_REQ:
wpas_dpp_rx_pkex_exchange_req(wpa_s, src, buf, len, freq);
break;
case DPP_PA_PKEX_EXCHANGE_RESP:
wpas_dpp_rx_pkex_exchange_resp(wpa_s, src, buf, len, freq);
break;
case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
wpas_dpp_rx_pkex_commit_reveal_req(wpa_s, src, buf, len, freq);
break;
case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, buf, len, freq);
break;
default:
wpa_printf(MSG_DEBUG,
"DPP: Ignored unsupported frame subtype %d", type);
@ -1614,6 +1850,113 @@ int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
}
int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd)
{
struct dpp_bootstrap_info *own_bi;
const char *pos, *end;
unsigned int wait_time;
pos = os_strstr(cmd, " own=");
if (!pos)
return -1;
pos += 5;
own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos));
if (!own_bi) {
wpa_printf(MSG_DEBUG,
"DPP: Identified bootstrap info not found");
return -1;
}
if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
wpa_printf(MSG_DEBUG,
"DPP: Identified bootstrap info not for PKEX");
return -1;
}
wpa_s->dpp_pkex_bi = own_bi;
os_free(wpa_s->dpp_pkex_identifier);
wpa_s->dpp_pkex_identifier = NULL;
pos = os_strstr(cmd, " identifier=");
if (pos) {
pos += 12;
end = os_strchr(pos, ' ');
if (!end)
return -1;
wpa_s->dpp_pkex_identifier = os_malloc(end - pos + 1);
if (!wpa_s->dpp_pkex_identifier)
return -1;
os_memcpy(wpa_s->dpp_pkex_identifier, pos, end - pos);
wpa_s->dpp_pkex_identifier[end - pos] = '\0';
}
pos = os_strstr(cmd, " code=");
if (!pos)
return -1;
os_free(wpa_s->dpp_pkex_code);
wpa_s->dpp_pkex_code = os_strdup(pos + 6);
if (!wpa_s->dpp_pkex_code)
return -1;
if (os_strstr(cmd, " init=1")) {
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
dpp_pkex_free(wpa_s->dpp_pkex);
wpa_s->dpp_pkex = dpp_pkex_init(own_bi, wpa_s->own_addr,
wpa_s->dpp_pkex_identifier,
wpa_s->dpp_pkex_code);
if (!wpa_s->dpp_pkex)
return -1;
msg = wpa_s->dpp_pkex->exchange_req;
wait_time = wpa_s->max_remain_on_chan;
if (wait_time > 2000)
wait_time = 2000;
/* TODO: Which channel to use? */
offchannel_send_action(wpa_s, 2437, broadcast, wpa_s->own_addr,
broadcast,
wpabuf_head(msg), wpabuf_len(msg),
wait_time, wpas_dpp_tx_pkex_status, 0);
}
/* TODO: Support multiple PKEX info entries */
os_free(wpa_s->dpp_pkex_auth_cmd);
wpa_s->dpp_pkex_auth_cmd = os_strdup(cmd);
return 1;
}
int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id)
{
unsigned int id_val;
if (os_strcmp(id, "*") == 0) {
id_val = 0;
} else {
id_val = atoi(id);
if (id_val == 0)
return -1;
}
if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code)
return -1;
/* TODO: Support multiple PKEX entries */
os_free(wpa_s->dpp_pkex_code);
wpa_s->dpp_pkex_code = NULL;
os_free(wpa_s->dpp_pkex_identifier);
wpa_s->dpp_pkex_identifier = NULL;
os_free(wpa_s->dpp_pkex_auth_cmd);
wpa_s->dpp_pkex_auth_cmd = NULL;
wpa_s->dpp_pkex_bi = NULL;
/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
dpp_pkex_free(wpa_s->dpp_pkex);
wpa_s->dpp_pkex = NULL;
return 0;
}
int wpas_dpp_init(struct wpa_supplicant *wpa_s)
{
u8 adv_proto_id[7];
@ -1657,5 +2000,7 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
dpp_configurator_del(wpa_s, 0);
dpp_auth_deinit(wpa_s->dpp_auth);
wpa_s->dpp_auth = NULL;
wpas_dpp_pkex_remove(wpa_s, "*");
wpa_s->dpp_pkex = NULL;
os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN);
}

View file

@ -27,6 +27,8 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
const u8 *buf, size_t len, unsigned int freq);
int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd);
int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id);
int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd);
int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id);
int wpas_dpp_init(struct wpa_supplicant *wpa_s);
void wpas_dpp_deinit(struct wpa_supplicant *wpa_s);
int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,

View file

@ -2904,6 +2904,20 @@ static int wpa_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, int argc,
return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
}
#endif /* CONFIG_DPP */
@ -3550,6 +3564,12 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "dpp_configurator_remove", wpa_cli_cmd_dpp_configurator_remove, NULL,
cli_cmd_flag_none,
"*|<id> = remove DPP configurator" },
{ "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL,
cli_cmd_flag_sensitive,
"add PKEX code" },
{ "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
cli_cmd_flag_none,
"*|<id> = remove DPP pkex information" },
#endif /* CONFIG_DPP */
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};

View file

@ -1172,6 +1172,11 @@ struct wpa_supplicant {
int dpp_gas_client;
u8 dpp_intro_bssid[ETH_ALEN];
void *dpp_intro_network;
struct dpp_pkex *dpp_pkex;
struct dpp_bootstrap_info *dpp_pkex_bi;
char *dpp_pkex_code;
char *dpp_pkex_identifier;
char *dpp_pkex_auth_cmd;
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;