// Copyright 2020 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "esp_dpp_i.h" #include "esp_dpp.h" #include "esp_wpa.h" #include "esp_timer.h" #include "esp_event.h" #include "esp_wifi.h" #include "common/ieee802_11_defs.h" static void *s_dpp_task_hdl = NULL; static void *s_dpp_evt_queue = NULL; static void *s_dpp_api_lock = NULL; static bool s_dpp_stop_listening; static int s_dpp_auth_retries; struct esp_dpp_context_t s_dpp_ctx; static wifi_action_rx_cb_t s_action_rx_cb = esp_supp_rx_action; #define DPP_API_LOCK() xSemaphoreTakeRecursive(s_dpp_api_lock, portMAX_DELAY) #define DPP_API_UNLOCK() xSemaphoreGiveRecursive(s_dpp_api_lock) struct action_rx_param { u8 sa[ETH_ALEN]; u32 channel; u32 frm_len; u32 vendor_data_len; struct ieee80211_action *action_frm; }; static int esp_dpp_post_evt(uint32_t evt_id, uint32_t data) { DPP_API_LOCK(); dpp_event_t *evt = os_zalloc(sizeof(dpp_event_t)); if (evt == NULL) { DPP_API_UNLOCK(); return ESP_ERR_NO_MEM; } evt->id = evt_id; evt->data = data; if ( xQueueSend(s_dpp_evt_queue, &evt, 10 / portTICK_PERIOD_MS ) != pdPASS) { DPP_API_UNLOCK(); os_free(evt); return ESP_ERR_DPP_FAILURE; } DPP_API_UNLOCK(); return ESP_OK; } static void esp_dpp_call_cb(esp_supp_dpp_event_t evt, void *data) { s_dpp_ctx.dpp_event_cb(evt, data); } void esp_send_action_frame(uint8_t *dest_mac, const uint8_t *buf, uint32_t len, uint8_t channel, uint32_t wait_time_ms) { wifi_action_tx_req_t *req = os_zalloc(sizeof(*req) + len);; if (!req) { return; } req->ifx = WIFI_IF_STA; memcpy(req->dest_mac, dest_mac, ETH_ALEN); req->no_ack = false; req->data_len = len; req->rx_cb = s_action_rx_cb; memcpy(req->data, buf, req->data_len); wpa_printf(MSG_DEBUG, "DPP: Mgmt Tx - MAC:" MACSTR ", Channel-%d, WaitT-%d", MAC2STR(dest_mac), channel, wait_time_ms); if (ESP_OK != esp_wifi_action_tx_req(WIFI_OFFCHAN_TX_REQ, channel, wait_time_ms, req)) { wpa_printf(MSG_ERROR, "DPP: Failed to perfrm offchannel operation"); esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_TX_FAILURE); os_free(req); return; } os_free(req); } static void esp_dpp_rx_auth_req(struct action_rx_param *rx_param, uint8_t *dpp_data) { size_t len = rx_param->vendor_data_len - 2; const u8 *r_bootstrap, *i_bootstrap; u16 r_bootstrap_len, i_bootstrap_len; struct dpp_bootstrap_info *own_bi; int rc; wpa_printf(MSG_INFO, "DPP: Authentication Request from " MACSTR, MAC2STR(rx_param->sa)); r_bootstrap = dpp_get_attr(dpp_data, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, &r_bootstrap_len); if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { wpa_printf(MSG_INFO, "DPP: Missing or invalid Responder Bootstrapping Key Hash attribute"); rc = ESP_ERR_DPP_INVALID_ATTR; goto fail; } wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", r_bootstrap, r_bootstrap_len); i_bootstrap = dpp_get_attr(dpp_data, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, &i_bootstrap_len); if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { wpa_printf(MSG_INFO, "DPP: Missing or invalid Initiator Bootstrapping Key Hash attribute"); rc = ESP_ERR_DPP_INVALID_ATTR; goto fail; } wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", i_bootstrap, i_bootstrap_len); own_bi = dpp_bootstrap_get_id(s_dpp_ctx.dpp_global, s_dpp_ctx.id); /* Try to find own and peer bootstrapping key matches based on the * received hash values */ if (os_memcmp(own_bi->pubkey_hash, r_bootstrap, SHA256_MAC_LEN)) { wpa_printf(MSG_INFO, "DPP: No matching own bootstrapping key found as responder - ignore message"); rc = ESP_ERR_DPP_INVALID_ATTR; goto fail; } s_dpp_ctx.dpp_auth = dpp_auth_req_rx(NULL, DPP_CAPAB_ENROLLEE, 0, NULL, own_bi, rx_param->channel, (const u8 *)&rx_param->action_frm->u.public_action.v, dpp_data, len); os_memcpy(s_dpp_ctx.dpp_auth->peer_mac_addr, rx_param->sa, ETH_ALEN); esp_send_action_frame(rx_param->sa, wpabuf_head(s_dpp_ctx.dpp_auth->resp_msg), wpabuf_len(s_dpp_ctx.dpp_auth->resp_msg), rx_param->channel, OFFCHAN_TX_WAIT_TIME); return; fail: esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)rc); } static void gas_query_req_tx(struct dpp_authentication *auth) { struct wpabuf *buf; int supp_op_classes[] = {81, 0}; buf = dpp_build_conf_req_helper(auth, NULL, 0, NULL, supp_op_classes); if (!buf) { wpa_printf(MSG_DEBUG, "DPP: No configuration request data available"); esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_FAILURE); return; } wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (chan %u)", MAC2STR(auth->peer_mac_addr), auth->curr_chan); esp_send_action_frame(auth->peer_mac_addr, wpabuf_head(buf), wpabuf_len(buf), auth->curr_chan, OFFCHAN_TX_WAIT_TIME); } static int esp_dpp_handle_config_obj(struct dpp_authentication *auth, struct dpp_config_obj *conf) { wifi_config_t *wifi_cfg = &s_dpp_ctx.wifi_cfg; if (conf->ssid_len) { os_memcpy(wifi_cfg->sta.ssid, conf->ssid, conf->ssid_len); } if (dpp_akm_legacy(conf->akm)) { if (conf->passphrase[0]) os_memcpy(wifi_cfg->sta.password, conf->passphrase, sizeof(wifi_cfg->sta.password)); if (conf->akm == DPP_AKM_PSK_SAE) { wifi_cfg->sta.pmf_cfg.capable = true; wifi_cfg->sta.pmf_cfg.required = true; } } if (conf->connector) { /* TODO: Save the Connector and consider using a command * to fetch the value instead of sending an event with * it. The Connector could end up being larger than what * most clients are ready to receive as an event * message. */ wpa_printf(MSG_INFO, DPP_EVENT_CONNECTOR "%s", conf->connector); } s_dpp_stop_listening = false; esp_wifi_action_tx_req(WIFI_OFFCHAN_TX_CANCEL, 0, 0, NULL); esp_dpp_call_cb(ESP_SUPP_DPP_CFG_RECVD, wifi_cfg); return 0; } static void esp_dpp_rx_auth_conf(struct action_rx_param *rx_param, uint8_t *dpp_data) { struct dpp_authentication *auth = s_dpp_ctx.dpp_auth; struct ieee80211_public_action *public_action = &rx_param->action_frm->u.public_action; size_t len = rx_param->vendor_data_len - 2; int rc; wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, MAC2STR(rx_param->sa)); if (!auth) { wpa_printf(MSG_DEBUG, "DPP: No DPP Authentication in progress - drop"); rc = ESP_ERR_DPP_FAILURE; goto fail; } if (os_memcmp(rx_param->sa, auth->peer_mac_addr, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); rc = ESP_ERR_DPP_FAILURE; goto fail; } if (dpp_auth_conf_rx(auth, (const u8 *)&public_action->v, dpp_data, len) < 0) { wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); rc = ESP_ERR_DPP_FAILURE; goto fail; } /* Send GAS Query Req */ gas_query_req_tx(auth); return; fail: esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)rc); } static void esp_dpp_rx_auth(struct action_rx_param *rx_param) { uint8_t crypto_suit, type; uint8_t *tmp; tmp = rx_param->action_frm->u.public_action.v.pa_vendor_spec.vendor_data; crypto_suit = tmp[0]; type = tmp[1]; if (crypto_suit != 1) { wpa_printf(MSG_ERROR, "DPP: Unsupported crypto suit"); esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_NOT_SUPPORTED); return; } switch (type) { case DPP_PA_AUTHENTICATION_REQ: esp_dpp_rx_auth_req(rx_param, &tmp[2]); break; case DPP_PA_AUTHENTICATION_CONF: esp_dpp_rx_auth_conf(rx_param, &tmp[2]); break; } } static void gas_query_resp_rx(struct action_rx_param *rx_param) { struct dpp_authentication *auth = s_dpp_ctx.dpp_auth; uint8_t *pos = rx_param->action_frm->u.public_action.v.pa_gas_resp.data; uint8_t *resp = &pos[10]; int i, res; if (pos[1] == WLAN_EID_VENDOR_SPECIFIC && pos[2] == 5 && WPA_GET_BE24(&pos[3]) == OUI_WFA && pos[6] == 0x1a && pos[7] == 1) { if (dpp_conf_resp_rx(auth, resp, rx_param->vendor_data_len - 2) < 0) { wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); goto fail; } for (i = 0; i < auth->num_conf_obj; i++) { res = esp_dpp_handle_config_obj(auth, &auth->conf_obj[i]); if (res < 0) { goto fail; } } } return; fail: esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_FAILURE); } static void esp_dpp_rx_action(struct action_rx_param *rx_param) { if (rx_param->action_frm->category == WLAN_ACTION_PUBLIC) { struct ieee80211_public_action *public_action = &rx_param->action_frm->u.public_action; wpa_printf(MSG_DEBUG, "DPP: Rx Public Action frame: action - %d", public_action->action); if (public_action->action == WLAN_PA_VENDOR_SPECIFIC && WPA_GET_BE24(public_action->v.pa_vendor_spec.oui) == OUI_WFA && public_action->v.pa_vendor_spec.wfa_stype == DPP_OUI_TYPE) { rx_param->vendor_data_len = rx_param->frm_len - (size_t)(public_action->v.pa_vendor_spec.vendor_data - (u8 *)rx_param->action_frm); if (!s_dpp_stop_listening) { esp_supp_dpp_stop_listen(); } esp_dpp_rx_auth(rx_param); } else if (public_action->action == WLAN_PA_GAS_INITIAL_RESP && public_action->v.pa_gas_resp.type == WLAN_EID_ADV_PROTO && public_action->v.pa_gas_resp.length == 8 && public_action->v.pa_gas_resp.status_code == 0) { rx_param->vendor_data_len = rx_param->frm_len - (size_t)(public_action->v.pa_gas_resp.data + public_action->v.pa_gas_resp.length - (u8 *)rx_param->action_frm); gas_query_resp_rx(rx_param); } } os_free(rx_param->action_frm); os_free(rx_param); } static void esp_dpp_task(void *pvParameters ) { dpp_event_t *evt; bool task_del = false; for (;;) { if (xQueueReceive(s_dpp_evt_queue, &evt, portMAX_DELAY) == pdTRUE) { if (evt->id < SIG_DPP_MAX) { DPP_API_LOCK(); } else { os_free(evt); continue; } switch (evt->id) { case SIG_DPP_DEL_TASK: task_del = true; break; case SIG_DPP_BOOTSTRAP_GEN: { char *command = (char *)evt->data; const char *uri; s_dpp_ctx.id = dpp_bootstrap_gen(s_dpp_ctx.dpp_global, command); uri = dpp_bootstrap_get_uri(s_dpp_ctx.dpp_global, s_dpp_ctx.id); esp_dpp_call_cb(ESP_SUPP_DPP_URI_READY, (void *)uri); os_free(command); } break; case SIG_DPP_RX_ACTION: { esp_dpp_rx_action((struct action_rx_param *)evt->data); } break; case SIG_DPP_LISTEN_NEXT_CHANNEL: { struct dpp_bootstrap_params_t *p = &s_dpp_ctx.bootstrap_params; static int counter; int channel; channel = p->chan_list[counter++ % p->num_chan]; esp_wifi_remain_on_channel(WIFI_IF_STA, WIFI_ROC_REQ, channel, BOOTSTRAP_ROC_WAIT_TIME, s_action_rx_cb); } break; default: break; } os_free(evt); DPP_API_UNLOCK(); if (task_del) { break; } } } vQueueDelete(s_dpp_evt_queue); s_dpp_evt_queue = NULL; if (s_dpp_api_lock) { vSemaphoreDelete(s_dpp_api_lock); s_dpp_api_lock = NULL; } /* At this point, we completed */ vTaskDelete(NULL); } int esp_supp_rx_action(uint8_t *hdr, uint8_t *payload, size_t len, uint8_t channel) { struct ieee80211_hdr *rx_hdr = (struct ieee80211_hdr *)hdr; struct action_rx_param *rx_param; int ret = ESP_ERR_NOT_SUPPORTED; if (WLAN_FC_GET_STYPE(rx_hdr->frame_control) == WLAN_FC_STYPE_ACTION) { rx_param = os_zalloc(sizeof(struct action_rx_param)); if (!rx_param) { wpa_printf(MSG_ERROR, "Failed to allocate memory for Rx Action"); return ESP_ERR_NO_MEM; } os_memcpy(rx_param->sa, rx_hdr->addr2, ETH_ALEN); rx_param->channel = channel; rx_param->action_frm = os_zalloc(len); if (!rx_param->action_frm) { wpa_printf(MSG_ERROR, "Failed to allocate memory for Rx Action"); os_free(rx_param); return ESP_ERR_NO_MEM; } rx_param->frm_len = len; os_memcpy(rx_param->action_frm, payload, len); ret = esp_dpp_post_evt(SIG_DPP_RX_ACTION, (u32)rx_param); if (ESP_OK != ret) { wpa_printf(MSG_ERROR, "Failed to post event to DPP Task(status=%d)", ret); os_free(rx_param->action_frm); os_free(rx_param); return ret; } } return ret; } static void offchan_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_id == WIFI_EVENT_ACTION_TX_STATUS) { wifi_event_action_tx_status_t *evt = (wifi_event_action_tx_status_t *)event_data; wpa_printf(MSG_DEBUG, "Mgmt Tx Status - %d, Cookie - 0x%x", evt->status, (uint32_t)evt->context); if (evt->status) { esp_dpp_call_cb(ESP_SUPP_DPP_FAIL, (void *)ESP_ERR_DPP_TX_FAILURE); } } else if (event_id == WIFI_EVENT_ROC_DONE) { wifi_event_roc_done_t *evt = (wifi_event_roc_done_t *)event_data; if (!s_dpp_stop_listening && evt->context == (uint32_t)s_action_rx_cb) { esp_dpp_post_evt(SIG_DPP_LISTEN_NEXT_CHANNEL, 0); } } } static char *esp_dpp_parse_chan_list(const char *chan_list) { struct dpp_bootstrap_params_t *params = &s_dpp_ctx.bootstrap_params; char *uri_channels = os_zalloc(14 * 6 + 1); const char *pos = chan_list; const char *pos2; char *pos3 = uri_channels; params->num_chan = 0; os_memcpy(pos3, " chan=", strlen(" chan=")); pos3 += strlen(" chan="); while (pos && *pos) { int channel; int len = strlen(chan_list); pos2 = pos; while (*pos2 >= '0' && *pos2 <= '9') { pos2++; } if (*pos2 == ',' || *pos2 == ' ' || *pos2 == '\0') { channel = atoi(pos); if (channel < 1 || channel > 14) { os_free(uri_channels); return NULL; } params->chan_list[params->num_chan++] = channel; os_memcpy(pos3, "81/", strlen("81/")); pos3 += strlen("81/"); os_memcpy(pos3, pos, (pos2 - pos)); pos3 += (pos2 - pos); *pos3++ = ','; pos = pos2 + 1; } while (*pos == ',' || *pos == ' ' || *pos == '\0') { pos++; } if (((int)(pos - chan_list) >= len)) { break; } } *(pos3 - 1) = ' '; return uri_channels; } esp_err_t esp_supp_dpp_bootstrap_gen(const char *chan_list, enum dpp_bootstrap_type type, const char *key, const char *uri_info) { struct dpp_bootstrap_params_t *params = &s_dpp_ctx.bootstrap_params; char *uri_chan_list = esp_dpp_parse_chan_list(chan_list); char *command = os_zalloc(1200); int ret; if (!uri_chan_list || !command || params->num_chan >= 14 || params->num_chan == 0) { wpa_printf(MSG_ERROR, "Invalid Channel list - %s", chan_list); if (command) { os_free(command); } ret = ESP_ERR_DPP_FAILURE; goto fail; } if (type != DPP_BOOTSTRAP_QR_CODE) { wpa_printf(MSG_INFO, "Bootstrap type %d not supported", type); os_free(command); ret = ESP_ERR_NOT_SUPPORTED; goto fail; } params->type = type; esp_wifi_get_mac(WIFI_IF_STA, params->mac); if (uri_info) { params->info_len = strlen(uri_info); if (params->info_len) { params->info = os_zalloc(params->info_len + 1); if (!params->info) { os_free(command); ret = ESP_ERR_NO_MEM; goto fail; } os_memcpy(params->info, uri_info, params->info_len); } } if (key) { params->key_len = strlen(key); if (params->key_len) { char prefix[] = "30310201010420"; char postfix[] = "a00a06082a8648ce3d030107"; params->key = os_zalloc(params->key_len + sizeof(prefix) + sizeof(postfix)); if (!params->key) { os_free(command); ret = ESP_ERR_NO_MEM; goto fail; } sprintf(params->key, "%s%s%s", prefix, key, postfix); } } sprintf(command, "type=qrcode mac=" MACSTR "%s%s%s%s%s", MAC2STR(params->mac), uri_chan_list, params->key_len ? "key=" : "", params->key_len ? params->key : "", params->info_len ? " info=" : "", params->info_len ? params->info : ""); ret = esp_dpp_post_evt(SIG_DPP_BOOTSTRAP_GEN, (u32)command); if (ret != ESP_OK) { os_free(command); if (params->info) { os_free(params->info); params->info = NULL; } if (params->key) { os_free(params->key); params->key = NULL; } goto fail; } ret = ESP_OK; fail: if (uri_chan_list) { os_free(uri_chan_list); } return ret; } esp_err_t esp_supp_dpp_start_listen(void) { if (esp_wifi_get_user_init_flag_internal() == 0) { wpa_printf(MSG_ERROR, "DPP: ROC not possible before wifi is started"); return ESP_ERR_INVALID_STATE; } return esp_dpp_post_evt(SIG_DPP_LISTEN_NEXT_CHANNEL, 0); } void esp_supp_dpp_stop_listen(void) { s_dpp_stop_listening = true; esp_wifi_remain_on_channel(WIFI_IF_STA, WIFI_ROC_CANCEL, 0, 0, NULL); } esp_err_t esp_supp_dpp_init(esp_supp_dpp_event_cb_t cb) { struct dpp_global_config cfg = {0}; os_bzero(&s_dpp_ctx, sizeof(s_dpp_ctx)); s_dpp_ctx.dpp_event_cb = cb; cfg.cb_ctx = &s_dpp_ctx; cfg.msg_ctx = &s_dpp_ctx; s_dpp_ctx.dpp_global = dpp_global_init(&cfg); s_dpp_stop_listening = false; s_dpp_evt_queue = xQueueCreate(3, sizeof(dpp_event_t)); xTaskCreate(esp_dpp_task, "dppT", DPP_TASK_STACK_SIZE, NULL, 2, s_dpp_task_hdl); s_dpp_api_lock = xSemaphoreCreateRecursiveMutex(); if (!s_dpp_api_lock) { wpa_printf(MSG_ERROR, "DPP: dpp_init: failed to create DPP API lock"); return ESP_ERR_NO_MEM; } esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_ACTION_TX_STATUS, &offchan_event_handler, NULL); esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_ROC_DONE, &offchan_event_handler, NULL); wpa_printf(MSG_INFO, "esp_dpp_task prio:%d, stack:%d\n", 2, DPP_TASK_STACK_SIZE); return ESP_OK; } void esp_supp_dpp_deinit(void) { struct dpp_bootstrap_params_t *params = &s_dpp_ctx.bootstrap_params; if (params->info) { os_free(params->info); params->info = NULL; } if (params->key) { os_free(params->key); params->key = NULL; } s_dpp_auth_retries = 0; dpp_global_deinit(s_dpp_ctx.dpp_global); esp_dpp_post_evt(SIG_DPP_DEL_TASK, 0); }