From 37bccfcab854a60ef54823e60109b9b563416964 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 6 Jul 2022 17:11:12 +0300 Subject: [PATCH] DPP3: Push button bootstrap mechanism Add support to use a push button -based bootstrap mechanism with DPP. The new DPP_PUSH_BUTTON control interface command enables this mode on the AP/hostapd and station/wpa_supplicant. This goes through the following sequence of events: a suitable peer in active push button mode is discovered with session overlap detection, PKEX is executed with bootstrap key hash validation, DPP authentication and configuration exchanges are performed. Signed-off-by: Jouni Malinen --- hostapd/ctrl_iface.c | 18 ++ hostapd/hostapd_cli.c | 13 + src/ap/dpp_hostapd.c | 462 +++++++++++++++++++++++++++++- src/ap/dpp_hostapd.h | 2 + src/ap/hostapd.h | 18 ++ src/common/dpp.c | 61 ++++ src/common/dpp.h | 13 +- src/common/dpp_crypto.c | 18 +- src/common/dpp_i.h | 6 +- src/common/dpp_pkex.c | 28 +- src/common/dpp_tcp.c | 1 + src/common/wpa_ctrl.h | 2 + wpa_supplicant/ctrl_iface.c | 5 + wpa_supplicant/dpp_supplicant.c | 397 ++++++++++++++++++++++++- wpa_supplicant/dpp_supplicant.h | 2 + wpa_supplicant/wpa_cli.c | 14 + wpa_supplicant/wpa_supplicant_i.h | 16 ++ 17 files changed, 1047 insertions(+), 29 deletions(-) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 4e464da73..e0b582030 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -3674,6 +3674,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) { hostapd_dpp_chirp_stop(hapd); #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + } else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) { + if (hostapd_dpp_push_button(hapd) < 0) + reply_len = -1; +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ #ifdef RADIUS_SERVER } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) { @@ -4182,6 +4187,19 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) #ifdef CONFIG_DPP dpp_global_clear(interfaces->dpp); +#ifdef CONFIG_DPP3 + { + int i; + + for (i = 0; i < DPP_PB_INFO_COUNT; i++) { + struct dpp_pb_info *info; + + info = &interfaces->dpp_pb[i]; + info->rx_time.sec = 0; + info->rx_time.usec = 0; + } + } +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ } diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 60396f3da..7ab978179 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1503,6 +1503,15 @@ static int hostapd_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc, } #endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_DPP3 +static int hostapd_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_PUSH_BUTTON"); +} +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ @@ -1729,6 +1738,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL, "= stop DPP chirp" }, #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + { "dpp_push_button", hostapd_cli_cmd_dpp_push_button, NULL, + "= press DPP push button" }, +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL, "=Add/Delete/Show/Clear accept MAC ACL" }, diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 2613c0cbb..1c4be2430 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -15,6 +15,7 @@ #include "common/dpp.h" #include "common/gas.h" #include "common/wpa_ctrl.h" +#include "crypto/random.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "gas_query_ap.h" @@ -362,7 +363,7 @@ static int hostapd_dpp_pkex_init(struct hostapd_data *hapd, hapd->dpp_pkex = NULL; pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr, hapd->dpp_pkex_identifier, - hapd->dpp_pkex_code, v2); + hapd->dpp_pkex_code, hapd->dpp_pkex_code_len, v2); if (!pkex) return -1; pkex->forced_ver = ver != PKEX_VER_AUTO; @@ -1450,11 +1451,53 @@ static void hostapd_dpp_conn_status_result_wait_timeout(void *eloop_ctx, } +#ifdef CONFIG_DPP3 + +static bool hostapd_dpp_pb_active(struct hostapd_data *hapd) +{ + struct hapd_interfaces *ifaces = hapd->iface->interfaces; + + return ifaces && (ifaces->dpp_pb_time.sec || + ifaces->dpp_pb_time.usec); +} + + +static void hostapd_dpp_remove_pb_hash(struct hostapd_data *hapd) +{ + struct hapd_interfaces *ifaces = hapd->iface->interfaces; + int i; + + if (!ifaces->dpp_pb_bi) + return; + for (i = 0; i < DPP_PB_INFO_COUNT; i++) { + struct dpp_pb_info *info = &ifaces->dpp_pb[i]; + + if (info->rx_time.sec == 0 && info->rx_time.usec == 0) + continue; + if (os_memcmp(info->hash, ifaces->dpp_pb_resp_hash, + SHA256_MAC_LEN) == 0) { + /* Allow a new push button session to be established + * immediately without the successfully completed + * session triggering session overlap. */ + info->rx_time.sec = 0; + info->rx_time.usec = 0; + wpa_printf(MSG_DEBUG, + "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning"); + } + } +} + +#endif /* CONFIG_DPP3 */ + + static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, const u8 *hdr, const u8 *buf, size_t len) { struct dpp_authentication *auth = hapd->dpp_auth; enum dpp_status_error status; +#ifdef CONFIG_DPP3 + struct hapd_interfaces *ifaces = hapd->iface->interfaces; +#endif /* CONFIG_DPP3 */ wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR, MAC2STR(src)); @@ -1498,6 +1541,20 @@ static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src, hapd->dpp_auth = NULL; eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd, NULL); +#ifdef CONFIG_DPP3 + if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) { + if (status == DPP_STATUS_OK) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT + "success"); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT + "no-configuration-available"); + ifaces->dpp_pb_result_indicated = true; + if (status == DPP_STATUS_OK) + hostapd_dpp_remove_pb_hash(hapd); + hostapd_dpp_push_button_stop(hapd); + } +#endif /* CONFIG_DPP3 */ } @@ -2048,6 +2105,7 @@ hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src, hapd->own_addr, src, hapd->dpp_pkex_identifier, hapd->dpp_pkex_code, + hapd->dpp_pkex_code_len, buf, len, v2); if (!hapd->dpp_pkex) { wpa_printf(MSG_DEBUG, @@ -2177,6 +2235,7 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, const u8 *hdr, const u8 *buf, size_t len, unsigned int freq) { + struct hapd_interfaces *ifaces = hapd->iface->interfaces; int res; struct dpp_bootstrap_info *bi; struct dpp_pkex *pkex = hapd->dpp_pkex; @@ -2196,11 +2255,33 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, return; } - bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq); + bi = dpp_pkex_finish(ifaces->dpp, pkex, src, freq); if (!bi) return; hapd->dpp_pkex = NULL; +#ifdef CONFIG_DPP3 + if (ifaces->dpp_pb_bi && + os_memcmp(bi->pubkey_hash_chirp, ifaces->dpp_pb_resp_hash, + SHA256_MAC_LEN) != 0) { + char id[20]; + + wpa_printf(MSG_INFO, + "DPP: Peer bootstrap key from PKEX does not match PB announcement hash"); + wpa_hexdump(MSG_DEBUG, + "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX", + bi->pubkey_hash_chirp, SHA256_MAC_LEN); + wpa_hexdump(MSG_DEBUG, + "DPP: Peer provided bootstrap key hash(chirp) from PB announcement", + ifaces->dpp_pb_resp_hash, SHA256_MAC_LEN); + + os_snprintf(id, sizeof(id), "%u", bi->id); + dpp_bootstrap_remove(ifaces->dpp, id); + hostapd_dpp_push_button_stop(hapd); + return; + } +#endif /* CONFIG_DPP3 */ + os_snprintf(cmd, sizeof(cmd), " peer=%u %s", bi->id, hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : ""); @@ -2215,6 +2296,292 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, } +#ifdef CONFIG_DPP3 + +static void hostapd_dpp_pb_pkex_init(struct hostapd_data *hapd, + unsigned int freq, const u8 *src, + const u8 *r_hash) +{ + struct hapd_interfaces *ifaces = hapd->iface->interfaces; + struct dpp_pkex *pkex; + struct wpabuf *msg; + char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL; + char cmd[300]; + const char *password = NULL; + struct sae_password_entry *e; + int conf_id = -1; + bool sae = false, psk = false; + + if (hapd->dpp_pkex) { + wpa_printf(MSG_DEBUG, + "PDP: Sending previously generated PKEX Exchange Request to " + MACSTR, MAC2STR(src)); + msg = hapd->dpp_pkex->exchange_req; + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with " + MACSTR, MAC2STR(src)); + + hapd->dpp_pkex_bi = ifaces->dpp_pb_bi; + os_memcpy(ifaces->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN); + + pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr, + "PBPKEX", (const char *) ifaces->dpp_pb_c_nonce, + ifaces->dpp_pb_bi->curve->nonce_len, + true); + if (!pkex) { + hostapd_dpp_push_button_stop(hapd); + return; + } + pkex->freq = freq; + + hapd->dpp_pkex = pkex; + msg = hapd->dpp_pkex->exchange_req; + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_EXCHANGE_REQ); + hostapd_drv_send_action(hapd, pkex->freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + pkex->exch_req_wait_time = 2000; + pkex->exch_req_tries = 1; + + wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex), + (const u8 *) hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + /* TODO: If a local Configurator has been enabled, allow a + * DPP AKM credential to be provisioned by setting conf_id. */ + } + + if (hapd->conf->wpa & WPA_PROTO_RSN) { + psk = hapd->conf->wpa_key_mgmt & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_PSK_SHA256); +#ifdef CONFIG_SAE + sae = hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE; +#endif /* CONFIG_SAE */ + } + +#ifdef CONFIG_SAE + for (e = hapd->conf->sae_passwords; sae && e && !password; + e = e->next) { + if (e->identifier || !is_broadcast_ether_addr(e->peer_addr)) + continue; + password = e->password; + } +#endif /* CONFIG_SAE */ + if (!password && hapd->conf->ssid.wpa_passphrase_set && + hapd->conf->ssid.wpa_passphrase) + password = hapd->conf->ssid.wpa_passphrase; + if (password) { + size_t len = 2 * os_strlen(password) + 1; + + pass_hex = os_malloc(len); + if (!pass_hex) { + hostapd_dpp_push_button_stop(hapd); + return; + } + wpa_snprintf_hex(pass_hex, len, (const u8 *) password, + os_strlen(password)); + } + + if (conf_id > 0 && sae && psk && pass_hex) { + os_snprintf(cmd, sizeof(cmd), + "conf=sta-dpp+psk+sae configurator=%d ssid=%s pass=%s", + conf_id, ssid_hex, pass_hex); + } else if (conf_id > 0 && sae && pass_hex) { + os_snprintf(cmd, sizeof(cmd), + "conf=sta-dpp+sae configurator=%d ssid=%s pass=%s", + conf_id, ssid_hex, pass_hex); + } else if (conf_id > 0) { + os_snprintf(cmd, sizeof(cmd), + "conf=sta-dpp configurator=%d ssid=%s", + conf_id, ssid_hex); + } if (sae && psk && pass_hex) { + os_snprintf(cmd, sizeof(cmd), + "conf=sta-psk+sae ssid=%s pass=%s", + ssid_hex, pass_hex); + } else if (sae && pass_hex) { + os_snprintf(cmd, sizeof(cmd), + "conf=sta-sae ssid=%s pass=%s", + ssid_hex, pass_hex); + } else if (psk && pass_hex) { + os_snprintf(cmd, sizeof(cmd), + "conf=sta-psk ssid=%s pass=%s", + ssid_hex, pass_hex); + } else { + wpa_printf(MSG_INFO, + "DPP: Unsupported AP configuration for push button"); + str_clear_free(pass_hex); + hostapd_dpp_push_button_stop(hapd); + return; + } + str_clear_free(pass_hex); + + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = os_strdup(cmd); + forced_memzero(cmd, sizeof(cmd)); + if (!hapd->dpp_pkex_auth_cmd) { + hostapd_dpp_push_button_stop(hapd); + return; + } +} + + +static void +hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd, + const u8 *src, const u8 *hdr, + const u8 *buf, size_t len, + unsigned int freq) +{ + struct hapd_interfaces *ifaces = hapd->iface->interfaces; + const u8 *r_hash; + u16 r_hash_len; + unsigned int i; + bool found = false; + struct dpp_pb_info *info, *tmp; + struct os_reltime now, age; + struct wpabuf *msg; + + if (!ifaces) + return; + + os_get_reltime(&now); + wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from " + MACSTR, MAC2STR(src)); + + r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_hash_len); + if (!r_hash || r_hash_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_hash, r_hash_len); + + for (i = 0; i < DPP_PB_INFO_COUNT; i++) { + info = &ifaces->dpp_pb[i]; + if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) || + os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0) + continue; + wpa_printf(MSG_DEBUG, + "DPP: Active push button Enrollee already known"); + found = true; + info->rx_time = now; + } + + if (!found) { + for (i = 0; i < DPP_PB_INFO_COUNT; i++) { + tmp = &ifaces->dpp_pb[i]; + if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0) + continue; + + if (os_reltime_expired(&now, &tmp->rx_time, 120)) { + wpa_hexdump(MSG_DEBUG, + "DPP: Push button Enrollee hash expired", + tmp->hash, SHA256_MAC_LEN); + tmp->rx_time.sec = 0; + tmp->rx_time.usec = 0; + continue; + } + + wpa_hexdump(MSG_DEBUG, + "DPP: Push button session overlap with hash", + tmp->hash, SHA256_MAC_LEN); + if (!ifaces->dpp_pb_result_indicated && + hostapd_dpp_pb_active(hapd)) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_PB_RESULT "session-overlap"); + ifaces->dpp_pb_result_indicated = true; + } + hostapd_dpp_push_button_stop(hapd); + return; + } + + /* Replace the oldest entry */ + info = &ifaces->dpp_pb[0]; + for (i = 1; i < DPP_PB_INFO_COUNT; i++) { + tmp = &ifaces->dpp_pb[i]; + if (os_reltime_before(&tmp->rx_time, &info->rx_time)) + info = tmp; + } + wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee"); + os_memcpy(info->hash, r_hash, SHA256_MAC_LEN); + info->rx_time = now; + } + + if (!hostapd_dpp_pb_active(hapd)) { + wpa_printf(MSG_DEBUG, + "DPP: Discard message since own push button has not been pressed"); + return; + } + + if (ifaces->dpp_pb_announce_time.sec == 0 && + ifaces->dpp_pb_announce_time.usec == 0) { + /* Start a wait before allowing PKEX to be initiated */ + ifaces->dpp_pb_announce_time = now; + } + + if (!ifaces->dpp_pb_bi) { + int res; + + res = dpp_bootstrap_gen(ifaces->dpp, "type=pkex"); + if (res < 0) + return; + ifaces->dpp_pb_bi = dpp_bootstrap_get_id(ifaces->dpp, res); + if (!ifaces->dpp_pb_bi) + return; + + if (random_get_bytes(ifaces->dpp_pb_c_nonce, + ifaces->dpp_pb_bi->curve->nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate C-nonce"); + hostapd_dpp_push_button_stop(hapd); + return; + } + } + + /* Skip the response if one was sent within last 50 ms since the + * Enrollee is going to send out at least three announcement messages. + */ + os_reltime_sub(&now, &ifaces->dpp_pb_last_resp, &age); + if (age.sec == 0 && age.usec < 50000) { + wpa_printf(MSG_DEBUG, + "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one"); + return; + } + + msg = dpp_build_pb_announcement_resp( + ifaces->dpp_pb_bi, r_hash, ifaces->dpp_pb_c_nonce, + ifaces->dpp_pb_bi->curve->nonce_len); + if (!msg) { + hostapd_dpp_push_button_stop(hapd); + return; + } + + wpa_printf(MSG_DEBUG, + "DPP: Send Push Button Presence Announcement Response to " + MACSTR, MAC2STR(src)); + ifaces->dpp_pb_last_resp = now; + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + + if (os_reltime_expired(&now, &ifaces->dpp_pb_announce_time, 15)) + hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash); +} + +#endif /* CONFIG_DPP3 */ + + void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, const u8 *buf, size_t len, unsigned int freq) { @@ -2320,6 +2687,12 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, freq); break; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + case DPP_PA_PB_PRESENCE_ANNOUNCEMENT: + hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr, + buf, len, freq); + break; +#endif /* CONFIG_DPP3 */ default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -2388,6 +2761,9 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) { struct dpp_authentication *auth = hapd->dpp_auth; +#ifdef CONFIG_DPP3 + struct hapd_interfaces *ifaces = hapd->iface->interfaces; +#endif /* CONFIG_DPP3 */ if (!auth) return; @@ -2427,6 +2803,20 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; +#ifdef CONFIG_DPP3 + if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) { + if (ok) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT + "success"); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT + "could-not-connect"); + ifaces->dpp_pb_result_indicated = true; + if (ok) + hostapd_dpp_remove_pb_hash(hapd); + hostapd_dpp_push_button_stop(hapd); + } +#endif /* CONFIG_DPP3 */ } @@ -2528,6 +2918,7 @@ int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) hapd->dpp_pkex_code = os_strdup(pos + 6); if (!hapd->dpp_pkex_code) return -1; + hapd->dpp_pkex_code_len = os_strlen(hapd->dpp_pkex_code); pos = os_strstr(cmd, " ver="); if (pos) { @@ -2600,6 +2991,9 @@ void hostapd_dpp_stop(struct hostapd_data *hapd) hapd->dpp_auth = NULL; dpp_pkex_free(hapd->dpp_pkex); hapd->dpp_pkex = NULL; +#ifdef CONFIG_DPP3 + hostapd_dpp_push_button_stop(hapd); +#endif /* CONFIG_DPP3 */ } @@ -2698,6 +3092,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) #endif /* CONFIG_DPP2 */ #ifdef CONFIG_DPP3 eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL); + hostapd_dpp_push_button_stop(hapd); #endif /* CONFIG_DPP3 */ dpp_auth_deinit(hapd->dpp_auth); hapd->dpp_auth = NULL; @@ -2705,6 +3100,8 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd) hapd->dpp_pkex = NULL; os_free(hapd->dpp_configurator_params); hapd->dpp_configurator_params = NULL; + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = NULL; } @@ -3077,3 +3474,64 @@ void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi) } #endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_DPP3 + +static void hostapd_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + wpa_printf(MSG_DEBUG, "DPP: Active push button mode expired"); + hostapd_dpp_push_button_stop(hapd); +} + + +int hostapd_dpp_push_button(struct hostapd_data *hapd) +{ + struct hapd_interfaces *ifaces = hapd->iface->interfaces; + + if (!ifaces || !ifaces->dpp) + return -1; + os_get_reltime(&ifaces->dpp_pb_time); + ifaces->dpp_pb_announce_time.sec = 0; + ifaces->dpp_pb_announce_time.usec = 0; + eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire, + hapd, NULL); + + return 0; +} + + +void hostapd_dpp_push_button_stop(struct hostapd_data *hapd) +{ + struct hapd_interfaces *ifaces = hapd->iface->interfaces; + + if (!ifaces || !ifaces->dpp) + return; + eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL); + if (hostapd_dpp_pb_active(hapd)) { + wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode"); + if (!ifaces->dpp_pb_result_indicated) + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_PB_RESULT "failed"); + } + ifaces->dpp_pb_time.sec = 0; + ifaces->dpp_pb_time.usec = 0; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = NULL; + + if (ifaces->dpp_pb_bi) { + char id[20]; + + os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id); + dpp_bootstrap_remove(ifaces->dpp, id); + ifaces->dpp_pb_bi = NULL; + } + + ifaces->dpp_pb_result_indicated = false; +} + +#endif /* CONFIG_DPP3 */ diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h index 264d3e4c0..6b7716428 100644 --- a/src/ap/dpp_hostapd.h +++ b/src/ap/dpp_hostapd.h @@ -45,5 +45,7 @@ int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd); int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd); void hostapd_dpp_chirp_stop(struct hostapd_data *hapd); void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi); +int hostapd_dpp_push_button(struct hostapd_data *hapd); +void hostapd_dpp_push_button_stop(struct hostapd_data *hapd); #endif /* DPP_HOSTAPD_H */ diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index cbe03a060..2e9a89890 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -43,6 +43,13 @@ struct mesh_conf; #define CTRL_IFACE_COOKIE_LEN 8 #endif /* CONFIG_CTRL_IFACE_UDP */ +#define DPP_PB_INFO_COUNT 2 + +struct dpp_pb_info { + u8 hash[SHA256_MAC_LEN]; + struct os_reltime rx_time; +}; + struct hostapd_iface; struct hapd_interfaces { @@ -76,6 +83,16 @@ struct hapd_interfaces { #ifdef CONFIG_DPP struct dpp_global *dpp; +#ifdef CONFIG_DPP3 + struct os_reltime dpp_pb_time; + struct os_reltime dpp_pb_announce_time; + struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT]; + struct dpp_bootstrap_info *dpp_pb_bi; + u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN]; + u8 dpp_pb_resp_hash[SHA256_MAC_LEN]; + struct os_reltime dpp_pb_last_resp; + bool dpp_pb_result_indicated; +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ #ifdef CONFIG_CTRL_IFACE_UDP @@ -401,6 +418,7 @@ struct hostapd_data { struct dpp_pkex *dpp_pkex; struct dpp_bootstrap_info *dpp_pkex_bi; char *dpp_pkex_code; + size_t dpp_pkex_code_len; char *dpp_pkex_identifier; enum dpp_pkex_ver dpp_pkex_ver; char *dpp_pkex_auth_cmd; diff --git a/src/common/dpp.c b/src/common/dpp.c index e878f7256..9bbe71868 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -4950,3 +4950,64 @@ void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src, } #endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_DPP3 + +struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, + "DPP: Build Push Button Presence Announcement frame"); + + msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT, + 4 + SHA256_MAC_LEN); + if (!msg) + return NULL; + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, bi->pubkey_hash_chirp); + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Push Button Presence Announcement frame attributes", + msg); + return msg; +} + + +struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi, + const u8 *e_hash, + const u8 *c_nonce, + size_t c_nonce_len) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, + "DPP: Build Push Button Presence Announcement Response frame"); + + msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP, + 2 * (4 + SHA256_MAC_LEN) + 4 + c_nonce_len); + if (!msg) + return NULL; + + /* Initiator Bootstrapping Key Hash */ + wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, bi->pubkey_hash_chirp, SHA256_MAC_LEN); + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, e_hash); + + /* Configurator Nonce */ + wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE); + wpabuf_put_le16(msg, c_nonce_len); + wpabuf_put_data(msg, c_nonce, c_nonce_len); + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Push Button Presence Announcement Response frame attributes", + msg); + return msg; +} + +#endif /* CONFIG_DPP3 */ diff --git a/src/common/dpp.h b/src/common/dpp.h index 812298f60..c2cf0c2ef 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -56,6 +56,8 @@ enum dpp_public_action_frame_type { DPP_PA_RECONFIG_AUTH_RESP = 16, DPP_PA_RECONFIG_AUTH_CONF = 17, DPP_PA_PKEX_EXCHANGE_REQ = 18, + DPP_PA_PB_PRESENCE_ANNOUNCEMENT = 19, + DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP = 20, }; enum dpp_attribute_id { @@ -203,6 +205,7 @@ struct dpp_pkex { u8 peer_mac[ETH_ALEN]; char *identifier; char *code; + size_t code_len; struct crypto_ec_key *x; struct crypto_ec_key *y; u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; @@ -644,13 +647,13 @@ int dpp_get_connector_version(const char *connector); struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, const u8 *own_mac, const char *identifier, const char *code, - bool v2); + size_t code_len, bool v2); struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, struct dpp_bootstrap_info *bi, const u8 *own_mac, const u8 *peer_mac, const char *identifier, - const char *code, + const char *code, size_t code_len, const u8 *buf, size_t len, bool v2); struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, const u8 *peer_mac, @@ -759,6 +762,12 @@ struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi); void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src, unsigned int freq, const u8 *hash); +struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi); +struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi, + const u8 *e_hash, + const u8 *c_nonce, + size_t c_nonce_len); + struct dpp_global_config { void *cb_ctx; void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi); diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c index fb239f772..9c0dd35a8 100644 --- a/src/common/dpp_crypto.c +++ b/src/common/dpp_crypto.c @@ -1437,7 +1437,7 @@ dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, int init) struct crypto_ec_point * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init, - const char *code, const char *identifier, + const char *code, size_t code_len, const char *identifier, struct crypto_ec **ret_ec) { u8 hash[DPP_MAX_HASH_LEN]; @@ -1465,9 +1465,9 @@ dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init, len[num_elem] = os_strlen(identifier); num_elem++; } - wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, code_len); addr[num_elem] = (const u8 *) code; - len[num_elem] = os_strlen(code); + len[num_elem] = code_len; num_elem++; if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) goto fail; @@ -1512,7 +1512,7 @@ fail: struct crypto_ec_point * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp, - const char *code, const char *identifier, + const char *code, size_t code_len, const char *identifier, struct crypto_ec **ret_ec) { u8 hash[DPP_MAX_HASH_LEN]; @@ -1540,9 +1540,9 @@ dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp, len[num_elem] = os_strlen(identifier); num_elem++; } - wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, code_len); addr[num_elem] = (const u8 *) code; - len[num_elem] = os_strlen(code); + len[num_elem] = code_len; num_elem++; if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) goto fail; @@ -1590,7 +1590,7 @@ int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, u8 ver_init, u8 ver_resp, const u8 *Mx, size_t Mx_len, const u8 *Nx, size_t Nx_len, - const char *code, + const char *code, size_t code_len, const u8 *Kx, size_t Kx_len, u8 *z, unsigned int hash_len) { @@ -1615,7 +1615,7 @@ int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, info_len = 2 * ETH_ALEN; else info_len = 2; - info_len += Mx_len + Nx_len + os_strlen(code); + info_len += Mx_len + Nx_len + code_len; info = os_malloc(info_len); if (!info) return -1; @@ -1633,7 +1633,7 @@ int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, pos += Mx_len; os_memcpy(pos, Nx, Nx_len); pos += Nx_len; - os_memcpy(pos, code, os_strlen(code)); + os_memcpy(pos, code, code_len); /* HKDF-Expand(PRK, info, L) */ if (hash_len == 32) diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h index 10db4e818..f7184121c 100644 --- a/src/common/dpp_i.h +++ b/src/common/dpp_i.h @@ -113,17 +113,17 @@ int dpp_derive_pmkid(const struct dpp_curve_params *curve, struct crypto_ec_key *peer_key, u8 *pmkid); struct crypto_ec_point * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init, - const char *code, const char *identifier, + const char *code, size_t code_len, const char *identifier, struct crypto_ec **ret_ec); struct crypto_ec_point * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp, - const char *code, const char *identifier, + const char *code, size_t code_len, const char *identifier, struct crypto_ec **ret_ec); int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, u8 ver_init, u8 ver_resp, const u8 *Mx, size_t Mx_len, const u8 *Nx, size_t Nx_len, - const char *code, + const char *code, size_t code_len, const u8 *Kx, size_t Kx_len, u8 *z, unsigned int hash_len); int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth, diff --git a/src/common/dpp_pkex.c b/src/common/dpp_pkex.c index cf4fb6b5c..c4713a4fe 100644 --- a/src/common/dpp_pkex.c +++ b/src/common/dpp_pkex.c @@ -41,7 +41,7 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex, /* Qi = H([MAC-Initiator |] [identifier |] code) * Pi */ Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : pkex->own_mac, pkex->code, - pkex->identifier, &ec); + pkex->code_len, pkex->identifier, &ec); if (!Qi) goto fail; @@ -171,7 +171,7 @@ static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt) struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, const u8 *own_mac, const char *identifier, const char *code, - bool v2) + size_t code_len, bool v2) { struct dpp_pkex *pkex; @@ -196,9 +196,10 @@ struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, if (!pkex->identifier) goto fail; } - pkex->code = os_strdup(code); + pkex->code = os_memdup(code, code_len); if (!pkex->code) goto fail; + pkex->code_len = code_len; pkex->exchange_req = dpp_pkex_build_exchange_req(pkex, v2); if (!pkex->exchange_req) goto fail; @@ -340,7 +341,7 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, const u8 *own_mac, const u8 *peer_mac, const char *identifier, - const char *code, + const char *code, size_t code_len, const u8 *buf, size_t len, bool v2) { const u8 *attr_group, *attr_id, *attr_key; @@ -437,8 +438,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, } /* Qi = H([MAC-Initiator |] [identifier |] code) * Pi */ - Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : peer_mac, code, identifier, - &ec); + Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : peer_mac, code, code_len, + identifier, &ec); if (!Qi) goto fail; @@ -477,9 +478,10 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, if (!pkex->identifier) goto fail; } - pkex->code = os_strdup(code); + pkex->code = os_memdup(code, code_len); if (!pkex->code) goto fail; + pkex->code_len = code_len; os_memcpy(pkex->Mx, attr_key, attr_key_len / 2); @@ -495,8 +497,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, goto fail; /* Qr = H([MAC-Responder |] [identifier |] code) * Pr */ - Qr = dpp_pkex_derive_Qr(curve, v2 ? NULL : own_mac, code, identifier, - NULL); + Qr = dpp_pkex_derive_Qr(curve, v2 ? NULL : own_mac, code, code_len, + identifier, NULL); if (!Qr) goto fail; @@ -550,7 +552,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, pkex->peer_version, DPP_VERSION, pkex->Mx, curve->prime_len, pkex->Nx, curve->prime_len, pkex->code, - Kx, Kx_len, pkex->z, curve->hash_len); + pkex->code_len, Kx, Kx_len, pkex->z, + curve->hash_len); os_memset(Kx, 0, Kx_len); if (res < 0) goto fail; @@ -791,7 +794,8 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, /* Qr = H([MAC-Responder |] [identifier |] code) * Pr */ Qr = dpp_pkex_derive_Qr(curve, pkex->v2 ? NULL : pkex->peer_mac, - pkex->code, pkex->identifier, &ec); + pkex->code, pkex->code_len, pkex->identifier, + &ec); if (!Qr) goto fail; @@ -869,7 +873,7 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, DPP_VERSION, pkex->peer_version, pkex->Mx, curve->prime_len, attr_key /* N.x */, attr_key_len / 2, - pkex->code, Kx, Kx_len, + pkex->code, pkex->code_len, Kx, Kx_len, pkex->z, curve->hash_len); os_memset(Kx, 0, Kx_len); if (res < 0) diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c index c83fb2da4..5b1eeb449 100644 --- a/src/common/dpp_tcp.c +++ b/src/common/dpp_tcp.c @@ -1017,6 +1017,7 @@ static int dpp_controller_rx_pkex_exchange_req(struct dpp_connection *conn, NULL, NULL, ctrl->pkex_identifier, ctrl->pkex_code, + os_strlen(ctrl->pkex_code), buf, len, true); if (!conn->pkex) { wpa_printf(MSG_DEBUG, diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 055bf7309..51dd3e836 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -213,6 +213,8 @@ extern "C" { #define DPP_EVENT_CSR "DPP-CSR " #define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX " #define DPP_EVENT_CONF_NEEDED "DPP-CONF-NEEDED " +#define DPP_EVENT_PB_STATUS "DPP-PB-STATUS " +#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT " /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 7adb7687b..1c89e7d12 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -12453,6 +12453,11 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_dpp_ca_set(wpa_s, buf + 10) < 0) reply_len = -1; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + } else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) { + if (wpas_dpp_push_button(wpa_s) < 0) + reply_len = -1; +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ } else if (os_strncmp(buf, "MSCS ", 5) == 0) { if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5)) diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index caf3af800..88986af6c 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -1610,6 +1610,14 @@ static int wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, conf->server_name); #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + if (!wpa_s->dpp_pb_result_indicated) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "success"); + wpa_s->dpp_pb_result_indicated = true; + } + +#endif /* CONFIG_DPP3 */ + return wpas_dpp_process_config(wpa_s, auth, conf); } @@ -2777,7 +2785,8 @@ static int wpas_dpp_pkex_init(struct wpa_supplicant *wpa_s, wpa_s->dpp_pkex = NULL; pkex = dpp_pkex_init(wpa_s, wpa_s->dpp_pkex_bi, wpa_s->own_addr, wpa_s->dpp_pkex_identifier, - wpa_s->dpp_pkex_code, v2); + wpa_s->dpp_pkex_code, wpa_s->dpp_pkex_code_len, + v2); if (!pkex) return -1; pkex->forced_ver = ver != PKEX_VER_AUTO; @@ -2947,6 +2956,7 @@ wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src, wpa_s->own_addr, src, wpa_s->dpp_pkex_identifier, wpa_s->dpp_pkex_code, + wpa_s->dpp_pkex_code_len, buf, len, v2); if (!wpa_s->dpp_pkex) { wpa_printf(MSG_DEBUG, @@ -2954,6 +2964,14 @@ wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src, return; } +#ifdef CONFIG_DPP3 + if (wpa_s->dpp_pb_bi && wpa_s->dpp_pb_announcement) { + wpa_printf(MSG_DEBUG, + "DPP: Started PB PKEX (no more PB announcements)"); + wpabuf_free(wpa_s->dpp_pb_announcement); + wpa_s->dpp_pb_announcement = NULL; + } +#endif /* CONFIG_DPP3 */ wpa_s->dpp_pkex_wait_auth_req = false; msg = wpa_s->dpp_pkex->exchange_resp; wait_time = wpa_s->max_remain_on_chan; @@ -3021,7 +3039,31 @@ wpas_dpp_pkex_finish(struct wpa_supplicant *wpa_s, const u8 *peer, bi = dpp_pkex_finish(wpa_s->dpp, wpa_s->dpp_pkex, peer, freq); if (!bi) return NULL; + wpa_s->dpp_pkex = NULL; + +#ifdef CONFIG_DPP3 + if (wpa_s->dpp_pb_bi && + os_memcmp(bi->pubkey_hash_chirp, wpa_s->dpp_pb_init_hash, + SHA256_MAC_LEN) != 0) { + char id[20]; + + wpa_printf(MSG_INFO, + "DPP: Peer bootstrap key from PKEX does not match PB announcement response hash"); + wpa_hexdump(MSG_DEBUG, + "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX", + bi->pubkey_hash_chirp, SHA256_MAC_LEN); + wpa_hexdump(MSG_DEBUG, + "DPP: Peer provided bootstrap key hash(chirp) from PB announcement response", + wpa_s->dpp_pb_init_hash, SHA256_MAC_LEN); + + os_snprintf(id, sizeof(id), "%u", bi->id); + dpp_bootstrap_remove(wpa_s->dpp, id); + wpas_dpp_push_button_stop(wpa_s); + return NULL; + } +#endif /* CONFIG_DPP3 */ + return bi; } @@ -3118,6 +3160,91 @@ wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src, } +#ifdef CONFIG_DPP3 + +static void +wpas_dpp_rx_pb_presence_announcement_resp(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *hdr, + const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *i_hash, *r_hash, *c_nonce; + u16 i_hash_len, r_hash_len, c_nonce_len; + bool overlap = false; + + if (!wpa_s->dpp_pb_announcement || !wpa_s->dpp_pb_bi) { + wpa_printf(MSG_INFO, + "DPP: Not in active push button mode - discard Push Button Presence Announcement Response from " + MACSTR, MAC2STR(src)); + return; + } + + wpa_printf(MSG_DEBUG, + "DPP: Push Button Presence Announcement Response from " + MACSTR, MAC2STR(src)); + + i_hash = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_hash_len); + r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_hash_len); + c_nonce = dpp_get_attr(buf, len, DPP_ATTR_CONFIGURATOR_NONCE, + &c_nonce_len); + if (!i_hash || i_hash_len != SHA256_MAC_LEN || + !r_hash || r_hash_len != SHA256_MAC_LEN || + !c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", + i_hash, i_hash_len); + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_hash, r_hash_len); + wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator Nonce", + c_nonce, c_nonce_len); + + if (os_memcmp(r_hash, wpa_s->dpp_pb_bi->pubkey_hash_chirp, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, + "DPP: Unexpected push button Responder hash - abort"); + overlap = true; + } + + if (wpa_s->dpp_pb_resp_freq && + os_memcmp(i_hash, wpa_s->dpp_pb_init_hash, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, + "DPP: Push button session overlap detected - abort"); + overlap = true; + } + + if (overlap) { + if (!wpa_s->dpp_pb_result_indicated) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT + "session-overlap"); + wpa_s->dpp_pb_result_indicated = true; + } + wpas_dpp_push_button_stop(wpa_s); + return; + } + + if (!wpa_s->dpp_pb_resp_freq) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS + "discovered push button AP/Configurator " MACSTR, + MAC2STR(src)); + wpa_s->dpp_pb_resp_freq = freq; + os_memcpy(wpa_s->dpp_pb_init_hash, i_hash, SHA256_MAC_LEN); + os_memcpy(wpa_s->dpp_pb_c_nonce, c_nonce, c_nonce_len); + wpa_s->dpp_pb_c_nonce_len = c_nonce_len; + /* Stop announcement iterations after at least one more full + * round and one extra round for postponed session overlap + * detection. */ + wpa_s->dpp_pb_stop_iter = 3; + } +} + +#endif /* CONFIG_DPP3 */ + + void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *buf, size_t len, unsigned int freq) { @@ -3227,6 +3354,12 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, wpas_dpp_rx_reconfig_auth_conf(wpa_s, src, hdr, buf, len, freq); break; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + case DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP: + wpas_dpp_rx_pb_presence_announcement_resp(wpa_s, src, hdr, + buf, len, freq); + break; +#endif /* CONFIG_DPP3 */ default: wpa_printf(MSG_DEBUG, "DPP: Ignored unsupported frame subtype %d", type); @@ -3717,6 +3850,7 @@ int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd) wpa_s->dpp_pkex_code = os_strdup(pos + 6); if (!wpa_s->dpp_pkex_code) return -1; + wpa_s->dpp_pkex_code_len = os_strlen(wpa_s->dpp_pkex_code); pos = os_strstr(cmd, " ver="); if (pos) { @@ -3794,6 +3928,9 @@ void wpas_dpp_stop(struct wpa_supplicant *wpa_s) wpa_s->dpp_pkex_wait_auth_req = false; if (wpa_s->dpp_gas_client && wpa_s->dpp_gas_dialog_token >= 0) gas_query_stop(wpa_s->gas, wpa_s->dpp_gas_dialog_token); +#ifdef CONFIG_DPP3 + wpas_dpp_push_button_stop(wpa_s); +#endif /* CONFIG_DPP3 */ } @@ -4452,3 +4589,261 @@ int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd) } #endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_DPP3 + +#define DPP_PB_ANNOUNCE_PER_CHAN 3 + +static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq); +static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx); + + +static bool wpas_dpp_pb_chan_ok(int flags) +{ + return !(flags & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)); +} + + +static int wpas_dpp_pb_channels(struct wpa_supplicant *wpa_s) +{ + struct hostapd_hw_modes *mode; + int m, c; + + for (m = 0; m < wpa_s->hw.num_modes; m++) { + mode = &wpa_s->hw.modes[m]; + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + + if (!wpas_dpp_pb_chan_ok(chan->flag)) + continue; + int_array_add_unique(&wpa_s->dpp_pb_freqs, chan->freq); + } + } + + return wpa_s->dpp_pb_freqs ? 0 : -1; +} + + +static void wpas_dpp_pb_tx_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) +{ + if (result == OFFCHANNEL_SEND_ACTION_FAILED) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to send push button announcement on %d MHz", + freq); + if (eloop_register_timeout(0, 0, wpas_dpp_pb_next, + wpa_s, NULL) < 0) + wpas_dpp_push_button_stop(wpa_s); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Push button announcement on %d MHz sent", + freq); + if (wpa_s->dpp_pb_discovery_done) { + wpa_s->dpp_pb_announce_count = 0; + wpa_printf(MSG_DEBUG, + "DPP: Wait for push button announcement response and PKEX on %d MHz", + freq); + if (eloop_register_timeout(0, 500000, wpas_dpp_pb_next, + wpa_s, NULL) < 0) + wpas_dpp_push_button_stop(wpa_s); + return; + } else if (wpa_s->dpp_pb_announce_count >= DPP_PB_ANNOUNCE_PER_CHAN) { + wpa_printf(MSG_DEBUG, + "DPP: Wait for push button announcement response on %d MHz", + freq); + if (eloop_register_timeout(0, 50000, wpas_dpp_pb_next, + wpa_s, NULL) < 0) + wpas_dpp_push_button_stop(wpa_s); + return; + } + + if (wpas_dpp_pb_announce(wpa_s, freq) < 0) + wpas_dpp_push_button_stop(wpa_s); +} + + +static int wpas_dpp_pb_announce(struct wpa_supplicant *wpa_s, int freq) +{ + struct wpabuf *msg; + int type; + + msg = wpa_s->dpp_pb_announcement; + if (!msg) + return -1; + + wpa_s->dpp_pb_announce_count++; + wpa_printf(MSG_DEBUG, + "DPP: Send push button announcement %d/%d (%d MHz)", + wpa_s->dpp_pb_announce_count, DPP_PB_ANNOUNCE_PER_CHAN, + freq); + + type = DPP_PA_PB_PRESENCE_ANNOUNCEMENT; + if (wpa_s->dpp_pb_announce_count == 1) + wpa_msg(wpa_s, MSG_INFO, + DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(broadcast), freq, type); + if (offchannel_send_action( + wpa_s, freq, broadcast, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + 1000, wpas_dpp_pb_tx_status, 0) < 0) + return -1; + + return 0; +} + + +static void wpas_dpp_pb_next(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct os_reltime now; + int freq; + + if (!wpa_s->dpp_pb_freqs) + return; + + os_get_reltime(&now); + offchannel_send_action_done(wpa_s); + + if (os_reltime_expired(&now, &wpa_s->dpp_pb_time, 100)) { + wpa_printf(MSG_DEBUG, "DPP: Push button wait time expired"); + wpas_dpp_push_button_stop(wpa_s); + return; + } + + if (wpa_s->dpp_pb_freq_idx >= int_array_len(wpa_s->dpp_pb_freqs)) { + wpa_printf(MSG_DEBUG, + "DPP: Completed push button announcement round"); + wpa_s->dpp_pb_freq_idx = 0; + if (wpa_s->dpp_pb_stop_iter > 0) { + wpa_s->dpp_pb_stop_iter--; + + if (wpa_s->dpp_pb_stop_iter == 1) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS + "wait for AP/Configurator to allow PKEX to be initiated"); + if (eloop_register_timeout(10, 0, + wpas_dpp_pb_next, + wpa_s, NULL) < 0) { + wpas_dpp_push_button_stop(wpa_s); + return; + } + return; + } + + if (wpa_s->dpp_pb_stop_iter == 0) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_STATUS + "start push button PKEX responder on the discovered channel (%d MHz)", + wpa_s->dpp_pb_resp_freq); + wpa_s->dpp_pb_discovery_done = true; + + wpa_s->dpp_pkex_bi = wpa_s->dpp_pb_bi; + + os_free(wpa_s->dpp_pkex_code); + wpa_s->dpp_pkex_code = os_memdup( + wpa_s->dpp_pb_c_nonce, + wpa_s->dpp_pb_c_nonce_len); + wpa_s->dpp_pkex_code_len = + wpa_s->dpp_pb_c_nonce_len; + + os_free(wpa_s->dpp_pkex_identifier); + wpa_s->dpp_pkex_identifier = + os_strdup("PBPKEX"); + + if (!wpa_s->dpp_pkex_code || + !wpa_s->dpp_pkex_identifier) { + wpas_dpp_push_button_stop(wpa_s); + return; + } + + wpa_s->dpp_pkex_ver = PKEX_VER_ONLY_2; + + os_free(wpa_s->dpp_pkex_auth_cmd); + wpa_s->dpp_pkex_auth_cmd = NULL; + } + } + } + + if (wpa_s->dpp_pb_discovery_done) + freq = wpa_s->dpp_pb_resp_freq; + else + freq = wpa_s->dpp_pb_freqs[wpa_s->dpp_pb_freq_idx++]; + wpa_s->dpp_pb_announce_count = 0; + if (!wpa_s->dpp_pb_announcement) { + wpa_printf(MSG_DEBUG, "DPP: Push button announcements stopped"); + return; + } + if (wpas_dpp_pb_announce(wpa_s, freq) < 0) { + wpas_dpp_push_button_stop(wpa_s); + return; + } +} + + +int wpas_dpp_push_button(struct wpa_supplicant *wpa_s) +{ + int res; + + if (!wpa_s->dpp) + return -1; + wpas_dpp_push_button_stop(wpa_s); + wpas_dpp_stop(wpa_s); + wpas_dpp_chirp_stop(wpa_s); + + os_get_reltime(&wpa_s->dpp_pb_time); + + if (wpas_dpp_pb_channels(wpa_s) < 0) + return -1; + wpa_s->dpp_pb_freq_idx = 0; + + res = dpp_bootstrap_gen(wpa_s->dpp, "type=pkex"); + if (res < 0) + return -1; + wpa_s->dpp_pb_bi = dpp_bootstrap_get_id(wpa_s->dpp, res); + if (!wpa_s->dpp_pb_bi) + return -1; + + wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; + wpa_s->dpp_netrole = DPP_NETROLE_STA; + wpa_s->dpp_qr_mutual = 0; + wpa_s->dpp_pb_announcement = + dpp_build_pb_announcement(wpa_s->dpp_pb_bi); + if (!wpa_s->dpp_pb_announcement) + return -1; + + return eloop_register_timeout(0, 0, wpas_dpp_pb_next, wpa_s, NULL); +} + + +void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->dpp) + return; + os_free(wpa_s->dpp_pb_freqs); + wpa_s->dpp_pb_freqs = NULL; + wpabuf_free(wpa_s->dpp_pb_announcement); + wpa_s->dpp_pb_announcement = NULL; + if (wpa_s->dpp_pb_bi) { + char id[20]; + + os_snprintf(id, sizeof(id), "%u", wpa_s->dpp_pb_bi->id); + dpp_bootstrap_remove(wpa_s->dpp, id); + wpa_s->dpp_pb_bi = NULL; + if (!wpa_s->dpp_pb_result_indicated) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PB_RESULT "failed"); + } + + wpa_s->dpp_pb_resp_freq = 0; + wpa_s->dpp_pb_stop_iter = 0; + wpa_s->dpp_pb_discovery_done = false; + wpa_s->dpp_pb_result_indicated = false; + + eloop_cancel_timeout(wpas_dpp_pb_next, wpa_s, NULL); +} + +#endif /* CONFIG_DPP3 */ diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 2b03a5462..f9ccc6028 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -44,5 +44,7 @@ void wpas_dpp_chirp_stop(struct wpa_supplicant *wpa_s); int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_ca_set(struct wpa_supplicant *wpa_s, const char *cmd); int wpas_dpp_conf_set(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_push_button(struct wpa_supplicant *wpa_s); +void wpas_dpp_push_button_stop(struct wpa_supplicant *wpa_s); #endif /* DPP_SUPPLICANT_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 0e2315d25..ff39b5d12 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -3159,6 +3159,15 @@ static int wpa_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc, } #endif /* CONFIG_DPP2 */ + + +#ifdef CONFIG_DPP3 +static int wpa_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_PUSH_BUTTON"); +} +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ @@ -3994,6 +4003,11 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_none, "= stop DPP chirp" }, #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + { "dpp_push_button", wpa_cli_cmd_dpp_push_button, NULL, + cli_cmd_flag_none, + "= press DPP push button" }, +#endif /* CONFIG_DPP3 */ #endif /* CONFIG_DPP */ { "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none, "= list all BSS entries (scan results)" }, diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 301d5d56a..a349d6551 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1462,6 +1462,7 @@ struct wpa_supplicant { struct dpp_pkex *dpp_pkex; struct dpp_bootstrap_info *dpp_pkex_bi; char *dpp_pkex_code; + size_t dpp_pkex_code_len; char *dpp_pkex_identifier; enum dpp_pkex_ver dpp_pkex_ver; char *dpp_pkex_auth_cmd; @@ -1492,6 +1493,21 @@ struct wpa_supplicant { int dpp_reconfig_ssid_id; struct dpp_reconfig_id *dpp_reconfig_id; #endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + struct os_reltime dpp_pb_time; + int *dpp_pb_freqs; + unsigned int dpp_pb_freq_idx; + unsigned int dpp_pb_announce_count; + struct wpabuf *dpp_pb_announcement; + struct dpp_bootstrap_info *dpp_pb_bi; + unsigned int dpp_pb_resp_freq; + u8 dpp_pb_init_hash[SHA256_MAC_LEN]; + int dpp_pb_stop_iter; + bool dpp_pb_discovery_done; + u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN]; + size_t dpp_pb_c_nonce_len; + bool dpp_pb_result_indicated; +#endif /* CONFIG_DPP3 */ #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override; char *dpp_discovery_override; -- 2.47.2