185732ac8SCy Schubert /* 285732ac8SCy Schubert * DPP functionality shared between hostapd and wpa_supplicant 385732ac8SCy Schubert * Copyright (c) 2017, Qualcomm Atheros, Inc. 4c1d255d3SCy Schubert * Copyright (c) 2018-2020, The Linux Foundation 5*a90b9d01SCy Schubert * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. 685732ac8SCy Schubert * 785732ac8SCy Schubert * This software may be distributed under the terms of the BSD license. 885732ac8SCy Schubert * See README for more details. 985732ac8SCy Schubert */ 1085732ac8SCy Schubert 1185732ac8SCy Schubert #include "utils/includes.h" 1285732ac8SCy Schubert 1385732ac8SCy Schubert #include "utils/common.h" 1485732ac8SCy Schubert #include "utils/base64.h" 1585732ac8SCy Schubert #include "utils/json.h" 16*a90b9d01SCy Schubert #include "utils/ip_addr.h" 1785732ac8SCy Schubert #include "common/ieee802_11_common.h" 1885732ac8SCy Schubert #include "common/wpa_ctrl.h" 194bc52338SCy Schubert #include "common/gas.h" 20c1d255d3SCy Schubert #include "eap_common/eap_defs.h" 2185732ac8SCy Schubert #include "crypto/crypto.h" 2285732ac8SCy Schubert #include "crypto/random.h" 2385732ac8SCy Schubert #include "crypto/aes.h" 2485732ac8SCy Schubert #include "crypto/aes_siv.h" 2585732ac8SCy Schubert #include "drivers/driver.h" 2685732ac8SCy Schubert #include "dpp.h" 27c1d255d3SCy Schubert #include "dpp_i.h" 2885732ac8SCy Schubert 2985732ac8SCy Schubert 3085732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 3132a95656SCy Schubert #ifdef CONFIG_DPP3 3232a95656SCy Schubert int dpp_version_override = 3; 3332a95656SCy Schubert #elif defined(CONFIG_DPP2) 34c1d255d3SCy Schubert int dpp_version_override = 2; 35c1d255d3SCy Schubert #else 36c1d255d3SCy Schubert int dpp_version_override = 1; 37c1d255d3SCy Schubert #endif 3885732ac8SCy Schubert enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; 3985732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 4085732ac8SCy Schubert 4185732ac8SCy Schubert 42c1d255d3SCy Schubert void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) 4385732ac8SCy Schubert { 4485732ac8SCy Schubert wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); 4585732ac8SCy Schubert } 4685732ac8SCy Schubert 4785732ac8SCy Schubert 4885732ac8SCy Schubert struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, 4985732ac8SCy Schubert size_t len) 5085732ac8SCy Schubert { 5185732ac8SCy Schubert struct wpabuf *msg; 5285732ac8SCy Schubert 5385732ac8SCy Schubert msg = wpabuf_alloc(8 + len); 5485732ac8SCy Schubert if (!msg) 5585732ac8SCy Schubert return NULL; 5685732ac8SCy Schubert wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); 5785732ac8SCy Schubert wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); 5885732ac8SCy Schubert wpabuf_put_be24(msg, OUI_WFA); 5985732ac8SCy Schubert wpabuf_put_u8(msg, DPP_OUI_TYPE); 6085732ac8SCy Schubert wpabuf_put_u8(msg, 1); /* Crypto Suite */ 6185732ac8SCy Schubert wpabuf_put_u8(msg, type); 6285732ac8SCy Schubert return msg; 6385732ac8SCy Schubert } 6485732ac8SCy Schubert 6585732ac8SCy Schubert 6685732ac8SCy Schubert const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) 6785732ac8SCy Schubert { 6885732ac8SCy Schubert u16 id, alen; 6985732ac8SCy Schubert const u8 *pos = buf, *end = buf + len; 7085732ac8SCy Schubert 7185732ac8SCy Schubert while (end - pos >= 4) { 7285732ac8SCy Schubert id = WPA_GET_LE16(pos); 7385732ac8SCy Schubert pos += 2; 7485732ac8SCy Schubert alen = WPA_GET_LE16(pos); 7585732ac8SCy Schubert pos += 2; 7685732ac8SCy Schubert if (alen > end - pos) 7785732ac8SCy Schubert return NULL; 7885732ac8SCy Schubert if (id == req_id) { 7985732ac8SCy Schubert *ret_len = alen; 8085732ac8SCy Schubert return pos; 8185732ac8SCy Schubert } 8285732ac8SCy Schubert pos += alen; 8385732ac8SCy Schubert } 8485732ac8SCy Schubert 8585732ac8SCy Schubert return NULL; 8685732ac8SCy Schubert } 8785732ac8SCy Schubert 8885732ac8SCy Schubert 89c1d255d3SCy Schubert static const u8 * dpp_get_attr_next(const u8 *prev, const u8 *buf, size_t len, 90c1d255d3SCy Schubert u16 req_id, u16 *ret_len) 91c1d255d3SCy Schubert { 92c1d255d3SCy Schubert u16 id, alen; 93c1d255d3SCy Schubert const u8 *pos, *end = buf + len; 94c1d255d3SCy Schubert 95c1d255d3SCy Schubert if (!prev) 96c1d255d3SCy Schubert pos = buf; 97c1d255d3SCy Schubert else 98c1d255d3SCy Schubert pos = prev + WPA_GET_LE16(prev - 2); 99c1d255d3SCy Schubert while (end - pos >= 4) { 100c1d255d3SCy Schubert id = WPA_GET_LE16(pos); 101c1d255d3SCy Schubert pos += 2; 102c1d255d3SCy Schubert alen = WPA_GET_LE16(pos); 103c1d255d3SCy Schubert pos += 2; 104c1d255d3SCy Schubert if (alen > end - pos) 105c1d255d3SCy Schubert return NULL; 106c1d255d3SCy Schubert if (id == req_id) { 107c1d255d3SCy Schubert *ret_len = alen; 108c1d255d3SCy Schubert return pos; 109c1d255d3SCy Schubert } 110c1d255d3SCy Schubert pos += alen; 111c1d255d3SCy Schubert } 112c1d255d3SCy Schubert 113c1d255d3SCy Schubert return NULL; 114c1d255d3SCy Schubert } 115c1d255d3SCy Schubert 116c1d255d3SCy Schubert 11785732ac8SCy Schubert int dpp_check_attrs(const u8 *buf, size_t len) 11885732ac8SCy Schubert { 11985732ac8SCy Schubert const u8 *pos, *end; 12085732ac8SCy Schubert int wrapped_data = 0; 12185732ac8SCy Schubert 12285732ac8SCy Schubert pos = buf; 12385732ac8SCy Schubert end = buf + len; 12485732ac8SCy Schubert while (end - pos >= 4) { 12585732ac8SCy Schubert u16 id, alen; 12685732ac8SCy Schubert 12785732ac8SCy Schubert id = WPA_GET_LE16(pos); 12885732ac8SCy Schubert pos += 2; 12985732ac8SCy Schubert alen = WPA_GET_LE16(pos); 13085732ac8SCy Schubert pos += 2; 13185732ac8SCy Schubert wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", 13285732ac8SCy Schubert id, alen); 13385732ac8SCy Schubert if (alen > end - pos) { 13485732ac8SCy Schubert wpa_printf(MSG_DEBUG, 13585732ac8SCy Schubert "DPP: Truncated message - not enough room for the attribute - dropped"); 13685732ac8SCy Schubert return -1; 13785732ac8SCy Schubert } 13885732ac8SCy Schubert if (wrapped_data) { 13985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 14085732ac8SCy Schubert "DPP: An unexpected attribute included after the Wrapped Data attribute"); 14185732ac8SCy Schubert return -1; 14285732ac8SCy Schubert } 14385732ac8SCy Schubert if (id == DPP_ATTR_WRAPPED_DATA) 14485732ac8SCy Schubert wrapped_data = 1; 14585732ac8SCy Schubert pos += alen; 14685732ac8SCy Schubert } 14785732ac8SCy Schubert 14885732ac8SCy Schubert if (end != pos) { 14985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 15085732ac8SCy Schubert "DPP: Unexpected octets (%d) after the last attribute", 15185732ac8SCy Schubert (int) (end - pos)); 15285732ac8SCy Schubert return -1; 15385732ac8SCy Schubert } 15485732ac8SCy Schubert 15585732ac8SCy Schubert return 0; 15685732ac8SCy Schubert } 15785732ac8SCy Schubert 15885732ac8SCy Schubert 15985732ac8SCy Schubert void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) 16085732ac8SCy Schubert { 16185732ac8SCy Schubert if (!info) 16285732ac8SCy Schubert return; 16385732ac8SCy Schubert os_free(info->uri); 16485732ac8SCy Schubert os_free(info->info); 165c1d255d3SCy Schubert os_free(info->chan); 166*a90b9d01SCy Schubert os_free(info->host); 167c1d255d3SCy Schubert os_free(info->pk); 1684b72b91aSCy Schubert crypto_ec_key_deinit(info->pubkey); 169c1d255d3SCy Schubert str_clear_free(info->configurator_params); 17085732ac8SCy Schubert os_free(info); 17185732ac8SCy Schubert } 17285732ac8SCy Schubert 17385732ac8SCy Schubert 17485732ac8SCy Schubert const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) 17585732ac8SCy Schubert { 17685732ac8SCy Schubert switch (type) { 17785732ac8SCy Schubert case DPP_BOOTSTRAP_QR_CODE: 17885732ac8SCy Schubert return "QRCODE"; 17985732ac8SCy Schubert case DPP_BOOTSTRAP_PKEX: 18085732ac8SCy Schubert return "PKEX"; 181c1d255d3SCy Schubert case DPP_BOOTSTRAP_NFC_URI: 182c1d255d3SCy Schubert return "NFC-URI"; 18385732ac8SCy Schubert } 18485732ac8SCy Schubert return "??"; 18585732ac8SCy Schubert } 18685732ac8SCy Schubert 18785732ac8SCy Schubert 18885732ac8SCy Schubert static int dpp_uri_valid_info(const char *info) 18985732ac8SCy Schubert { 19085732ac8SCy Schubert while (*info) { 19185732ac8SCy Schubert unsigned char val = *info++; 19285732ac8SCy Schubert 19385732ac8SCy Schubert if (val < 0x20 || val > 0x7e || val == 0x3b) 19485732ac8SCy Schubert return 0; 19585732ac8SCy Schubert } 19685732ac8SCy Schubert 19785732ac8SCy Schubert return 1; 19885732ac8SCy Schubert } 19985732ac8SCy Schubert 20085732ac8SCy Schubert 20185732ac8SCy Schubert static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) 20285732ac8SCy Schubert { 20385732ac8SCy Schubert bi->uri = os_strdup(uri); 20485732ac8SCy Schubert return bi->uri ? 0 : -1; 20585732ac8SCy Schubert } 20685732ac8SCy Schubert 20785732ac8SCy Schubert 20885732ac8SCy Schubert int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, 20985732ac8SCy Schubert const char *chan_list) 21085732ac8SCy Schubert { 211206b73d0SCy Schubert const char *pos = chan_list, *pos2; 212206b73d0SCy Schubert int opclass = -1, channel, freq; 21385732ac8SCy Schubert 21485732ac8SCy Schubert while (pos && *pos && *pos != ';') { 215206b73d0SCy Schubert pos2 = pos; 216206b73d0SCy Schubert while (*pos2 >= '0' && *pos2 <= '9') 217206b73d0SCy Schubert pos2++; 218206b73d0SCy Schubert if (*pos2 == '/') { 21985732ac8SCy Schubert opclass = atoi(pos); 220206b73d0SCy Schubert pos = pos2 + 1; 221206b73d0SCy Schubert } 22285732ac8SCy Schubert if (opclass <= 0) 22385732ac8SCy Schubert goto fail; 22485732ac8SCy Schubert channel = atoi(pos); 22585732ac8SCy Schubert if (channel <= 0) 22685732ac8SCy Schubert goto fail; 22785732ac8SCy Schubert while (*pos >= '0' && *pos <= '9') 22885732ac8SCy Schubert pos++; 22985732ac8SCy Schubert freq = ieee80211_chan_to_freq(NULL, opclass, channel); 23085732ac8SCy Schubert wpa_printf(MSG_DEBUG, 23185732ac8SCy Schubert "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", 23285732ac8SCy Schubert opclass, channel, freq); 233c1d255d3SCy Schubert bi->channels_listed = true; 23485732ac8SCy Schubert if (freq < 0) { 23585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 23685732ac8SCy Schubert "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", 23785732ac8SCy Schubert opclass, channel); 23885732ac8SCy Schubert } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { 23985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 24085732ac8SCy Schubert "DPP: Too many channels in URI channel-list - ignore list"); 24185732ac8SCy Schubert bi->num_freq = 0; 24285732ac8SCy Schubert break; 24385732ac8SCy Schubert } else { 24485732ac8SCy Schubert bi->freq[bi->num_freq++] = freq; 24585732ac8SCy Schubert } 24685732ac8SCy Schubert 24785732ac8SCy Schubert if (*pos == ';' || *pos == '\0') 24885732ac8SCy Schubert break; 24985732ac8SCy Schubert if (*pos != ',') 25085732ac8SCy Schubert goto fail; 25185732ac8SCy Schubert pos++; 25285732ac8SCy Schubert } 25385732ac8SCy Schubert 25485732ac8SCy Schubert return 0; 25585732ac8SCy Schubert fail: 25685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); 25785732ac8SCy Schubert return -1; 25885732ac8SCy Schubert } 25985732ac8SCy Schubert 26085732ac8SCy Schubert 26185732ac8SCy Schubert int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) 26285732ac8SCy Schubert { 26385732ac8SCy Schubert if (!mac) 26485732ac8SCy Schubert return 0; 26585732ac8SCy Schubert 26685732ac8SCy Schubert if (hwaddr_aton2(mac, bi->mac_addr) < 0) { 26785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); 26885732ac8SCy Schubert return -1; 26985732ac8SCy Schubert } 27085732ac8SCy Schubert 27185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); 27285732ac8SCy Schubert 27385732ac8SCy Schubert return 0; 27485732ac8SCy Schubert } 27585732ac8SCy Schubert 27685732ac8SCy Schubert 27785732ac8SCy Schubert int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) 27885732ac8SCy Schubert { 27985732ac8SCy Schubert const char *end; 28085732ac8SCy Schubert 28185732ac8SCy Schubert if (!info) 28285732ac8SCy Schubert return 0; 28385732ac8SCy Schubert 28485732ac8SCy Schubert end = os_strchr(info, ';'); 28585732ac8SCy Schubert if (!end) 28685732ac8SCy Schubert end = info + os_strlen(info); 28785732ac8SCy Schubert bi->info = os_malloc(end - info + 1); 28885732ac8SCy Schubert if (!bi->info) 28985732ac8SCy Schubert return -1; 29085732ac8SCy Schubert os_memcpy(bi->info, info, end - info); 29185732ac8SCy Schubert bi->info[end - info] = '\0'; 29285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); 29385732ac8SCy Schubert if (!dpp_uri_valid_info(bi->info)) { 29485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); 29585732ac8SCy Schubert return -1; 29685732ac8SCy Schubert } 29785732ac8SCy Schubert 29885732ac8SCy Schubert return 0; 29985732ac8SCy Schubert } 30085732ac8SCy Schubert 30185732ac8SCy Schubert 302c1d255d3SCy Schubert int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version) 30385732ac8SCy Schubert { 304c1d255d3SCy Schubert #ifdef CONFIG_DPP2 305c1d255d3SCy Schubert if (!version || DPP_VERSION < 2) 306c1d255d3SCy Schubert return 0; 30785732ac8SCy Schubert 308c1d255d3SCy Schubert if (*version == '1') 309c1d255d3SCy Schubert bi->version = 1; 310c1d255d3SCy Schubert else if (*version == '2') 311c1d255d3SCy Schubert bi->version = 2; 31232a95656SCy Schubert else if (*version == '3') 31332a95656SCy Schubert bi->version = 3; 314c1d255d3SCy Schubert else 315c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unknown URI version"); 31685732ac8SCy Schubert 317c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: URI version: %d", bi->version); 318c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 31985732ac8SCy Schubert 320c1d255d3SCy Schubert return 0; 32185732ac8SCy Schubert } 32285732ac8SCy Schubert 32385732ac8SCy Schubert 32485732ac8SCy Schubert static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) 32585732ac8SCy Schubert { 32685732ac8SCy Schubert u8 *data; 32785732ac8SCy Schubert size_t data_len; 32885732ac8SCy Schubert int res; 329c1d255d3SCy Schubert const char *end; 33085732ac8SCy Schubert 33185732ac8SCy Schubert end = os_strchr(info, ';'); 33285732ac8SCy Schubert if (!end) 33385732ac8SCy Schubert return -1; 33485732ac8SCy Schubert 335c1d255d3SCy Schubert data = base64_decode(info, end - info, &data_len); 33685732ac8SCy Schubert if (!data) { 33785732ac8SCy Schubert wpa_printf(MSG_DEBUG, 33885732ac8SCy Schubert "DPP: Invalid base64 encoding on URI public-key"); 33985732ac8SCy Schubert return -1; 34085732ac8SCy Schubert } 34185732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", 34285732ac8SCy Schubert data, data_len); 34385732ac8SCy Schubert 344c1d255d3SCy Schubert res = dpp_get_subject_public_key(bi, data, data_len); 34585732ac8SCy Schubert os_free(data); 346c1d255d3SCy Schubert return res; 34785732ac8SCy Schubert } 34885732ac8SCy Schubert 34985732ac8SCy Schubert 350*a90b9d01SCy Schubert static int dpp_parse_uri_supported_curves(struct dpp_bootstrap_info *bi, 351*a90b9d01SCy Schubert const char *txt) 352*a90b9d01SCy Schubert { 353*a90b9d01SCy Schubert int val; 354*a90b9d01SCy Schubert 355*a90b9d01SCy Schubert if (!txt) 356*a90b9d01SCy Schubert return 0; 357*a90b9d01SCy Schubert 358*a90b9d01SCy Schubert val = hex2num(txt[0]); 359*a90b9d01SCy Schubert if (val < 0) 360*a90b9d01SCy Schubert return -1; 361*a90b9d01SCy Schubert bi->supported_curves = val; 362*a90b9d01SCy Schubert 363*a90b9d01SCy Schubert val = hex2num(txt[1]); 364*a90b9d01SCy Schubert if (val > 0) 365*a90b9d01SCy Schubert bi->supported_curves |= val << 4; 366*a90b9d01SCy Schubert 367*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: URI supported curves: 0x%x", 368*a90b9d01SCy Schubert bi->supported_curves); 369*a90b9d01SCy Schubert 370*a90b9d01SCy Schubert return 0; 371*a90b9d01SCy Schubert } 372*a90b9d01SCy Schubert 373*a90b9d01SCy Schubert 374*a90b9d01SCy Schubert static int dpp_parse_uri_host(struct dpp_bootstrap_info *bi, const char *txt) 375*a90b9d01SCy Schubert { 376*a90b9d01SCy Schubert const char *end; 377*a90b9d01SCy Schubert char *port; 378*a90b9d01SCy Schubert struct hostapd_ip_addr addr; 379*a90b9d01SCy Schubert char buf[100], *pos; 380*a90b9d01SCy Schubert 381*a90b9d01SCy Schubert if (!txt) 382*a90b9d01SCy Schubert return 0; 383*a90b9d01SCy Schubert 384*a90b9d01SCy Schubert end = os_strchr(txt, ';'); 385*a90b9d01SCy Schubert if (!end) 386*a90b9d01SCy Schubert end = txt + os_strlen(txt); 387*a90b9d01SCy Schubert if (end - txt > (int) sizeof(buf) - 1) 388*a90b9d01SCy Schubert return -1; 389*a90b9d01SCy Schubert os_memcpy(buf, txt, end - txt); 390*a90b9d01SCy Schubert buf[end - txt] = '\0'; 391*a90b9d01SCy Schubert 392*a90b9d01SCy Schubert bi->port = DPP_TCP_PORT; 393*a90b9d01SCy Schubert 394*a90b9d01SCy Schubert pos = buf; 395*a90b9d01SCy Schubert if (*pos == '[') { 396*a90b9d01SCy Schubert pos = &buf[1]; 397*a90b9d01SCy Schubert port = os_strchr(pos, ']'); 398*a90b9d01SCy Schubert if (!port) 399*a90b9d01SCy Schubert return -1; 400*a90b9d01SCy Schubert *port++ = '\0'; 401*a90b9d01SCy Schubert if (*port == ':') 402*a90b9d01SCy Schubert bi->port = atoi(port + 1); 403*a90b9d01SCy Schubert } 404*a90b9d01SCy Schubert 405*a90b9d01SCy Schubert if (hostapd_parse_ip_addr(pos, &addr) < 0) { 406*a90b9d01SCy Schubert if (buf[0] != '[') { 407*a90b9d01SCy Schubert port = os_strrchr(pos, ':'); 408*a90b9d01SCy Schubert if (port) { 409*a90b9d01SCy Schubert *port++ = '\0'; 410*a90b9d01SCy Schubert bi->port = atoi(port); 411*a90b9d01SCy Schubert } 412*a90b9d01SCy Schubert } 413*a90b9d01SCy Schubert if (hostapd_parse_ip_addr(pos, &addr) < 0) { 414*a90b9d01SCy Schubert wpa_printf(MSG_INFO, 415*a90b9d01SCy Schubert "DPP: Invalid IP address in URI host entry: %s", 416*a90b9d01SCy Schubert pos); 417*a90b9d01SCy Schubert return -1; 418*a90b9d01SCy Schubert } 419*a90b9d01SCy Schubert } 420*a90b9d01SCy Schubert os_free(bi->host); 421*a90b9d01SCy Schubert bi->host = os_memdup(&addr, sizeof(addr)); 422*a90b9d01SCy Schubert if (!bi->host) 423*a90b9d01SCy Schubert return -1; 424*a90b9d01SCy Schubert 425*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: host: %s port: %u", 426*a90b9d01SCy Schubert hostapd_ip_txt(bi->host, buf, sizeof(buf)), bi->port); 427*a90b9d01SCy Schubert 428*a90b9d01SCy Schubert return 0; 429*a90b9d01SCy Schubert } 430*a90b9d01SCy Schubert 431*a90b9d01SCy Schubert 43285732ac8SCy Schubert static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) 43385732ac8SCy Schubert { 43485732ac8SCy Schubert const char *pos = uri; 43585732ac8SCy Schubert const char *end; 43685732ac8SCy Schubert const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; 437*a90b9d01SCy Schubert const char *version = NULL, *supported_curves = NULL, *host = NULL; 43885732ac8SCy Schubert struct dpp_bootstrap_info *bi; 43985732ac8SCy Schubert 44085732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); 44185732ac8SCy Schubert 44285732ac8SCy Schubert if (os_strncmp(pos, "DPP:", 4) != 0) { 44385732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); 44485732ac8SCy Schubert return NULL; 44585732ac8SCy Schubert } 44685732ac8SCy Schubert pos += 4; 44785732ac8SCy Schubert 44885732ac8SCy Schubert for (;;) { 44985732ac8SCy Schubert end = os_strchr(pos, ';'); 45085732ac8SCy Schubert if (!end) 45185732ac8SCy Schubert break; 45285732ac8SCy Schubert 45385732ac8SCy Schubert if (end == pos) { 45485732ac8SCy Schubert /* Handle terminating ";;" and ignore unexpected ";" 45585732ac8SCy Schubert * for parsing robustness. */ 45685732ac8SCy Schubert pos++; 45785732ac8SCy Schubert continue; 45885732ac8SCy Schubert } 45985732ac8SCy Schubert 46085732ac8SCy Schubert if (pos[0] == 'C' && pos[1] == ':' && !chan_list) 46185732ac8SCy Schubert chan_list = pos + 2; 46285732ac8SCy Schubert else if (pos[0] == 'M' && pos[1] == ':' && !mac) 46385732ac8SCy Schubert mac = pos + 2; 46485732ac8SCy Schubert else if (pos[0] == 'I' && pos[1] == ':' && !info) 46585732ac8SCy Schubert info = pos + 2; 46685732ac8SCy Schubert else if (pos[0] == 'K' && pos[1] == ':' && !pk) 46785732ac8SCy Schubert pk = pos + 2; 468c1d255d3SCy Schubert else if (pos[0] == 'V' && pos[1] == ':' && !version) 469c1d255d3SCy Schubert version = pos + 2; 470*a90b9d01SCy Schubert else if (pos[0] == 'B' && pos[1] == ':' && !supported_curves) 471*a90b9d01SCy Schubert supported_curves = pos + 2; 472*a90b9d01SCy Schubert else if (pos[0] == 'H' && pos[1] == ':' && !host) 473*a90b9d01SCy Schubert host = pos + 2; 47485732ac8SCy Schubert else 47585732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, 47685732ac8SCy Schubert "DPP: Ignore unrecognized URI parameter", 47785732ac8SCy Schubert pos, end - pos); 47885732ac8SCy Schubert pos = end + 1; 47985732ac8SCy Schubert } 48085732ac8SCy Schubert 48185732ac8SCy Schubert if (!pk) { 48285732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: URI missing public-key"); 48385732ac8SCy Schubert return NULL; 48485732ac8SCy Schubert } 48585732ac8SCy Schubert 48685732ac8SCy Schubert bi = os_zalloc(sizeof(*bi)); 48785732ac8SCy Schubert if (!bi) 48885732ac8SCy Schubert return NULL; 48985732ac8SCy Schubert 49085732ac8SCy Schubert if (dpp_clone_uri(bi, uri) < 0 || 49185732ac8SCy Schubert dpp_parse_uri_chan_list(bi, chan_list) < 0 || 49285732ac8SCy Schubert dpp_parse_uri_mac(bi, mac) < 0 || 49385732ac8SCy Schubert dpp_parse_uri_info(bi, info) < 0 || 494c1d255d3SCy Schubert dpp_parse_uri_version(bi, version) < 0 || 495*a90b9d01SCy Schubert dpp_parse_uri_supported_curves(bi, supported_curves) < 0 || 496*a90b9d01SCy Schubert dpp_parse_uri_host(bi, host) < 0 || 49785732ac8SCy Schubert dpp_parse_uri_pk(bi, pk) < 0) { 49885732ac8SCy Schubert dpp_bootstrap_info_free(bi); 49985732ac8SCy Schubert bi = NULL; 50085732ac8SCy Schubert } 50185732ac8SCy Schubert 50285732ac8SCy Schubert return bi; 50385732ac8SCy Schubert } 50485732ac8SCy Schubert 50585732ac8SCy Schubert 506c1d255d3SCy Schubert void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status) 50785732ac8SCy Schubert { 50885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Status %d", status); 50985732ac8SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_STATUS); 51085732ac8SCy Schubert wpabuf_put_le16(msg, 1); 51185732ac8SCy Schubert wpabuf_put_u8(msg, status); 51285732ac8SCy Schubert } 51385732ac8SCy Schubert 51485732ac8SCy Schubert 515c1d255d3SCy Schubert void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash) 51685732ac8SCy Schubert { 51785732ac8SCy Schubert if (hash) { 51885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); 51985732ac8SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); 52085732ac8SCy Schubert wpabuf_put_le16(msg, SHA256_MAC_LEN); 52185732ac8SCy Schubert wpabuf_put_data(msg, hash, SHA256_MAC_LEN); 52285732ac8SCy Schubert } 52385732ac8SCy Schubert } 52485732ac8SCy Schubert 52585732ac8SCy Schubert 52685732ac8SCy Schubert static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, 52785732ac8SCy Schubert u16 num_modes, unsigned int freq) 52885732ac8SCy Schubert { 52985732ac8SCy Schubert u16 m; 53085732ac8SCy Schubert int c, flag; 53185732ac8SCy Schubert 53285732ac8SCy Schubert if (!own_modes || !num_modes) 53385732ac8SCy Schubert return 1; 53485732ac8SCy Schubert 53585732ac8SCy Schubert for (m = 0; m < num_modes; m++) { 53685732ac8SCy Schubert for (c = 0; c < own_modes[m].num_channels; c++) { 53785732ac8SCy Schubert if ((unsigned int) own_modes[m].channels[c].freq != 53885732ac8SCy Schubert freq) 53985732ac8SCy Schubert continue; 54085732ac8SCy Schubert flag = own_modes[m].channels[c].flag; 54185732ac8SCy Schubert if (!(flag & (HOSTAPD_CHAN_DISABLED | 54285732ac8SCy Schubert HOSTAPD_CHAN_NO_IR | 54385732ac8SCy Schubert HOSTAPD_CHAN_RADAR))) 54485732ac8SCy Schubert return 1; 54585732ac8SCy Schubert } 54685732ac8SCy Schubert } 54785732ac8SCy Schubert 54885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); 54985732ac8SCy Schubert return 0; 55085732ac8SCy Schubert } 55185732ac8SCy Schubert 55285732ac8SCy Schubert 55385732ac8SCy Schubert static int freq_included(const unsigned int freqs[], unsigned int num, 55485732ac8SCy Schubert unsigned int freq) 55585732ac8SCy Schubert { 55685732ac8SCy Schubert while (num > 0) { 55785732ac8SCy Schubert if (freqs[--num] == freq) 55885732ac8SCy Schubert return 1; 55985732ac8SCy Schubert } 56085732ac8SCy Schubert return 0; 56185732ac8SCy Schubert } 56285732ac8SCy Schubert 56385732ac8SCy Schubert 56485732ac8SCy Schubert static void freq_to_start(unsigned int freqs[], unsigned int num, 56585732ac8SCy Schubert unsigned int freq) 56685732ac8SCy Schubert { 56785732ac8SCy Schubert unsigned int i; 56885732ac8SCy Schubert 56985732ac8SCy Schubert for (i = 0; i < num; i++) { 57085732ac8SCy Schubert if (freqs[i] == freq) 57185732ac8SCy Schubert break; 57285732ac8SCy Schubert } 57385732ac8SCy Schubert if (i == 0 || i >= num) 57485732ac8SCy Schubert return; 57585732ac8SCy Schubert os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); 57685732ac8SCy Schubert freqs[0] = freq; 57785732ac8SCy Schubert } 57885732ac8SCy Schubert 57985732ac8SCy Schubert 58085732ac8SCy Schubert static int dpp_channel_intersect(struct dpp_authentication *auth, 58185732ac8SCy Schubert struct hostapd_hw_modes *own_modes, 58285732ac8SCy Schubert u16 num_modes) 58385732ac8SCy Schubert { 58485732ac8SCy Schubert struct dpp_bootstrap_info *peer_bi = auth->peer_bi; 58585732ac8SCy Schubert unsigned int i, freq; 58685732ac8SCy Schubert 58785732ac8SCy Schubert for (i = 0; i < peer_bi->num_freq; i++) { 58885732ac8SCy Schubert freq = peer_bi->freq[i]; 58985732ac8SCy Schubert if (freq_included(auth->freq, auth->num_freq, freq)) 59085732ac8SCy Schubert continue; 59185732ac8SCy Schubert if (dpp_channel_ok_init(own_modes, num_modes, freq)) 59285732ac8SCy Schubert auth->freq[auth->num_freq++] = freq; 59385732ac8SCy Schubert } 59485732ac8SCy Schubert if (!auth->num_freq) { 59585732ac8SCy Schubert wpa_printf(MSG_INFO, 59685732ac8SCy Schubert "DPP: No available channels for initiating DPP Authentication"); 59785732ac8SCy Schubert return -1; 59885732ac8SCy Schubert } 59985732ac8SCy Schubert auth->curr_freq = auth->freq[0]; 60085732ac8SCy Schubert return 0; 60185732ac8SCy Schubert } 60285732ac8SCy Schubert 60385732ac8SCy Schubert 60485732ac8SCy Schubert static int dpp_channel_local_list(struct dpp_authentication *auth, 60585732ac8SCy Schubert struct hostapd_hw_modes *own_modes, 60685732ac8SCy Schubert u16 num_modes) 60785732ac8SCy Schubert { 60885732ac8SCy Schubert u16 m; 60985732ac8SCy Schubert int c, flag; 61085732ac8SCy Schubert unsigned int freq; 61185732ac8SCy Schubert 61285732ac8SCy Schubert auth->num_freq = 0; 61385732ac8SCy Schubert 61485732ac8SCy Schubert if (!own_modes || !num_modes) { 61585732ac8SCy Schubert auth->freq[0] = 2412; 61685732ac8SCy Schubert auth->freq[1] = 2437; 61785732ac8SCy Schubert auth->freq[2] = 2462; 61885732ac8SCy Schubert auth->num_freq = 3; 61985732ac8SCy Schubert return 0; 62085732ac8SCy Schubert } 62185732ac8SCy Schubert 62285732ac8SCy Schubert for (m = 0; m < num_modes; m++) { 62385732ac8SCy Schubert for (c = 0; c < own_modes[m].num_channels; c++) { 62485732ac8SCy Schubert freq = own_modes[m].channels[c].freq; 62585732ac8SCy Schubert flag = own_modes[m].channels[c].flag; 62685732ac8SCy Schubert if (flag & (HOSTAPD_CHAN_DISABLED | 62785732ac8SCy Schubert HOSTAPD_CHAN_NO_IR | 62885732ac8SCy Schubert HOSTAPD_CHAN_RADAR)) 62985732ac8SCy Schubert continue; 63085732ac8SCy Schubert if (freq_included(auth->freq, auth->num_freq, freq)) 63185732ac8SCy Schubert continue; 63285732ac8SCy Schubert auth->freq[auth->num_freq++] = freq; 63385732ac8SCy Schubert if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { 63485732ac8SCy Schubert m = num_modes; 63585732ac8SCy Schubert break; 63685732ac8SCy Schubert } 63785732ac8SCy Schubert } 63885732ac8SCy Schubert } 63985732ac8SCy Schubert 64085732ac8SCy Schubert return auth->num_freq == 0 ? -1 : 0; 64185732ac8SCy Schubert } 64285732ac8SCy Schubert 64385732ac8SCy Schubert 644c1d255d3SCy Schubert int dpp_prepare_channel_list(struct dpp_authentication *auth, 645c1d255d3SCy Schubert unsigned int neg_freq, 646c1d255d3SCy Schubert struct hostapd_hw_modes *own_modes, u16 num_modes) 64785732ac8SCy Schubert { 64885732ac8SCy Schubert int res; 64985732ac8SCy Schubert char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; 65085732ac8SCy Schubert unsigned int i; 65185732ac8SCy Schubert 652c1d255d3SCy Schubert if (!own_modes) { 653c1d255d3SCy Schubert if (!neg_freq) 654c1d255d3SCy Schubert return -1; 655c1d255d3SCy Schubert auth->num_freq = 1; 656c1d255d3SCy Schubert auth->freq[0] = neg_freq; 657c1d255d3SCy Schubert auth->curr_freq = neg_freq; 658c1d255d3SCy Schubert return 0; 659c1d255d3SCy Schubert } 660c1d255d3SCy Schubert 66185732ac8SCy Schubert if (auth->peer_bi->num_freq > 0) 66285732ac8SCy Schubert res = dpp_channel_intersect(auth, own_modes, num_modes); 66385732ac8SCy Schubert else 66485732ac8SCy Schubert res = dpp_channel_local_list(auth, own_modes, num_modes); 66585732ac8SCy Schubert if (res < 0) 66685732ac8SCy Schubert return res; 66785732ac8SCy Schubert 66885732ac8SCy Schubert /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most 66985732ac8SCy Schubert * likely channels first. */ 67085732ac8SCy Schubert freq_to_start(auth->freq, auth->num_freq, 2462); 67185732ac8SCy Schubert freq_to_start(auth->freq, auth->num_freq, 2412); 67285732ac8SCy Schubert freq_to_start(auth->freq, auth->num_freq, 2437); 67385732ac8SCy Schubert 67485732ac8SCy Schubert auth->freq_idx = 0; 67585732ac8SCy Schubert auth->curr_freq = auth->freq[0]; 67685732ac8SCy Schubert 67785732ac8SCy Schubert pos = freqs; 67885732ac8SCy Schubert end = pos + sizeof(freqs); 67985732ac8SCy Schubert for (i = 0; i < auth->num_freq; i++) { 68085732ac8SCy Schubert res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); 68185732ac8SCy Schubert if (os_snprintf_error(end - pos, res)) 68285732ac8SCy Schubert break; 68385732ac8SCy Schubert pos += res; 68485732ac8SCy Schubert } 68585732ac8SCy Schubert *pos = '\0'; 68685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", 68785732ac8SCy Schubert freqs); 68885732ac8SCy Schubert 68985732ac8SCy Schubert return 0; 69085732ac8SCy Schubert } 69185732ac8SCy Schubert 69285732ac8SCy Schubert 693c1d255d3SCy Schubert int dpp_gen_uri(struct dpp_bootstrap_info *bi) 69485732ac8SCy Schubert { 695c1d255d3SCy Schubert char macstr[ETH_ALEN * 2 + 10]; 69685732ac8SCy Schubert size_t len; 697*a90b9d01SCy Schubert char supp_curves[10]; 698*a90b9d01SCy Schubert char host[100]; 69985732ac8SCy Schubert 70085732ac8SCy Schubert len = 4; /* "DPP:" */ 701c1d255d3SCy Schubert if (bi->chan) 702c1d255d3SCy Schubert len += 3 + os_strlen(bi->chan); /* C:...; */ 703c1d255d3SCy Schubert if (is_zero_ether_addr(bi->mac_addr)) 704c1d255d3SCy Schubert macstr[0] = '\0'; 705c1d255d3SCy Schubert else 706c1d255d3SCy Schubert os_snprintf(macstr, sizeof(macstr), "M:" COMPACT_MACSTR ";", 707c1d255d3SCy Schubert MAC2STR(bi->mac_addr)); 708c1d255d3SCy Schubert len += os_strlen(macstr); /* M:...; */ 709c1d255d3SCy Schubert if (bi->info) 710c1d255d3SCy Schubert len += 3 + os_strlen(bi->info); /* I:...; */ 711c1d255d3SCy Schubert #ifdef CONFIG_DPP2 712c1d255d3SCy Schubert len += 4; /* V:2; */ 713c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 714c1d255d3SCy Schubert len += 4 + os_strlen(bi->pk); /* K:...;; */ 715c1d255d3SCy Schubert 716*a90b9d01SCy Schubert if (bi->supported_curves) { 717*a90b9d01SCy Schubert u8 val = bi->supported_curves; 718*a90b9d01SCy Schubert 719*a90b9d01SCy Schubert if (val & 0xf0) { 720*a90b9d01SCy Schubert val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); 721*a90b9d01SCy Schubert len += os_snprintf(supp_curves, sizeof(supp_curves), 722*a90b9d01SCy Schubert "B:%02x;", val); 723*a90b9d01SCy Schubert } else { 724*a90b9d01SCy Schubert len += os_snprintf(supp_curves, sizeof(supp_curves), 725*a90b9d01SCy Schubert "B:%x;", val); 726*a90b9d01SCy Schubert } 727*a90b9d01SCy Schubert } else { 728*a90b9d01SCy Schubert supp_curves[0] = '\0'; 729*a90b9d01SCy Schubert } 730*a90b9d01SCy Schubert 731*a90b9d01SCy Schubert host[0] = '\0'; 732*a90b9d01SCy Schubert if (bi->host) { 733*a90b9d01SCy Schubert char buf[100]; 734*a90b9d01SCy Schubert const char *addr; 735*a90b9d01SCy Schubert 736*a90b9d01SCy Schubert addr = hostapd_ip_txt(bi->host, buf, sizeof(buf)); 737*a90b9d01SCy Schubert if (!addr) 738*a90b9d01SCy Schubert return -1; 739*a90b9d01SCy Schubert if (bi->port == DPP_TCP_PORT) 740*a90b9d01SCy Schubert len += os_snprintf(host, sizeof(host), "H:%s;", addr); 741*a90b9d01SCy Schubert else if (bi->host->af == AF_INET) 742*a90b9d01SCy Schubert len += os_snprintf(host, sizeof(host), "H:%s:%u;", 743*a90b9d01SCy Schubert addr, bi->port); 744*a90b9d01SCy Schubert else 745*a90b9d01SCy Schubert len += os_snprintf(host, sizeof(host), "H:[%s]:%u;", 746*a90b9d01SCy Schubert addr, bi->port); 747*a90b9d01SCy Schubert } 748*a90b9d01SCy Schubert 749c1d255d3SCy Schubert os_free(bi->uri); 75085732ac8SCy Schubert bi->uri = os_malloc(len + 1); 75185732ac8SCy Schubert if (!bi->uri) 75285732ac8SCy Schubert return -1; 753*a90b9d01SCy Schubert os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%s%sK:%s;;", 754c1d255d3SCy Schubert bi->chan ? "C:" : "", bi->chan ? bi->chan : "", 755c1d255d3SCy Schubert bi->chan ? ";" : "", 756c1d255d3SCy Schubert macstr, 757c1d255d3SCy Schubert bi->info ? "I:" : "", bi->info ? bi->info : "", 758c1d255d3SCy Schubert bi->info ? ";" : "", 75932a95656SCy Schubert DPP_VERSION == 3 ? "V:3;" : 76032a95656SCy Schubert (DPP_VERSION == 2 ? "V:2;" : ""), 761*a90b9d01SCy Schubert supp_curves, 762*a90b9d01SCy Schubert host, 763c1d255d3SCy Schubert bi->pk); 764c1d255d3SCy Schubert return 0; 76585732ac8SCy Schubert } 76685732ac8SCy Schubert 76785732ac8SCy Schubert 768c1d255d3SCy Schubert struct dpp_authentication * 769c1d255d3SCy Schubert dpp_alloc_auth(struct dpp_global *dpp, void *msg_ctx) 77085732ac8SCy Schubert { 77185732ac8SCy Schubert struct dpp_authentication *auth; 77285732ac8SCy Schubert 77385732ac8SCy Schubert auth = os_zalloc(sizeof(*auth)); 77485732ac8SCy Schubert if (!auth) 77585732ac8SCy Schubert return NULL; 776c1d255d3SCy Schubert auth->global = dpp; 77785732ac8SCy Schubert auth->msg_ctx = msg_ctx; 778c1d255d3SCy Schubert auth->conf_resp_status = 255; 77985732ac8SCy Schubert return auth; 78085732ac8SCy Schubert } 78185732ac8SCy Schubert 78285732ac8SCy Schubert 7834bc52338SCy Schubert static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, 78485732ac8SCy Schubert const char *json) 78585732ac8SCy Schubert { 78685732ac8SCy Schubert size_t nonce_len; 78785732ac8SCy Schubert size_t json_len, clear_len; 788*a90b9d01SCy Schubert struct wpabuf *clear = NULL, *msg = NULL, *pe = NULL; 78985732ac8SCy Schubert u8 *wrapped; 79085732ac8SCy Schubert size_t attr_len; 791*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 792*a90b9d01SCy Schubert u8 auth_i[DPP_MAX_HASH_LEN]; 793*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 79485732ac8SCy Schubert 79585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); 79685732ac8SCy Schubert 79785732ac8SCy Schubert nonce_len = auth->curve->nonce_len; 79885732ac8SCy Schubert if (random_get_bytes(auth->e_nonce, nonce_len)) { 79985732ac8SCy Schubert wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); 80085732ac8SCy Schubert goto fail; 80185732ac8SCy Schubert } 80285732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); 80385732ac8SCy Schubert json_len = os_strlen(json); 804c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: configRequest JSON", json, json_len); 80585732ac8SCy Schubert 80685732ac8SCy Schubert /* { E-nonce, configAttrib }ke */ 80785732ac8SCy Schubert clear_len = 4 + nonce_len + 4 + json_len; 808*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 809*a90b9d01SCy Schubert if (auth->waiting_new_key) { 810*a90b9d01SCy Schubert pe = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0); 811*a90b9d01SCy Schubert if (!pe) 812*a90b9d01SCy Schubert goto fail; 813*a90b9d01SCy Schubert clear_len += 4 + wpabuf_len(pe); 814*a90b9d01SCy Schubert 815*a90b9d01SCy Schubert if (dpp_derive_auth_i(auth, auth_i) < 0) 816*a90b9d01SCy Schubert goto fail; 817*a90b9d01SCy Schubert clear_len += 4 + auth->curve->hash_len; 818*a90b9d01SCy Schubert } 819*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 82085732ac8SCy Schubert clear = wpabuf_alloc(clear_len); 82185732ac8SCy Schubert attr_len = 4 + clear_len + AES_BLOCK_SIZE; 82285732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 82385732ac8SCy Schubert if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) 82485732ac8SCy Schubert attr_len += 5; 82585732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 82685732ac8SCy Schubert msg = wpabuf_alloc(attr_len); 82785732ac8SCy Schubert if (!clear || !msg) 82885732ac8SCy Schubert goto fail; 82985732ac8SCy Schubert 83085732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 83185732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { 83285732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); 83385732ac8SCy Schubert goto skip_e_nonce; 83485732ac8SCy Schubert } 83585732ac8SCy Schubert if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { 83685732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); 83785732ac8SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 83885732ac8SCy Schubert wpabuf_put_le16(clear, nonce_len - 1); 83985732ac8SCy Schubert wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); 84085732ac8SCy Schubert goto skip_e_nonce; 84185732ac8SCy Schubert } 84285732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { 84385732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 84485732ac8SCy Schubert goto skip_wrapped_data; 84585732ac8SCy Schubert } 84685732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 84785732ac8SCy Schubert 84885732ac8SCy Schubert /* E-nonce */ 84985732ac8SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 85085732ac8SCy Schubert wpabuf_put_le16(clear, nonce_len); 85185732ac8SCy Schubert wpabuf_put_data(clear, auth->e_nonce, nonce_len); 85285732ac8SCy Schubert 85385732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 85485732ac8SCy Schubert skip_e_nonce: 85585732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { 85685732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); 85785732ac8SCy Schubert goto skip_conf_attr_obj; 85885732ac8SCy Schubert } 85985732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 86085732ac8SCy Schubert 861*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 862*a90b9d01SCy Schubert if (pe) { 863*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Pe"); 864*a90b9d01SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_I_PROTOCOL_KEY); 865*a90b9d01SCy Schubert wpabuf_put_le16(clear, wpabuf_len(pe)); 866*a90b9d01SCy Schubert wpabuf_put_buf(clear, pe); 867*a90b9d01SCy Schubert } 868*a90b9d01SCy Schubert if (auth->waiting_new_key) { 869*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Initiator Authentication Tag"); 870*a90b9d01SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); 871*a90b9d01SCy Schubert wpabuf_put_le16(clear, auth->curve->hash_len); 872*a90b9d01SCy Schubert wpabuf_put_data(clear, auth_i, auth->curve->hash_len); 873*a90b9d01SCy Schubert } 874*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 875*a90b9d01SCy Schubert 87685732ac8SCy Schubert /* configAttrib */ 87785732ac8SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); 87885732ac8SCy Schubert wpabuf_put_le16(clear, json_len); 87985732ac8SCy Schubert wpabuf_put_data(clear, json, json_len); 88085732ac8SCy Schubert 88185732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 88285732ac8SCy Schubert skip_conf_attr_obj: 88385732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 88485732ac8SCy Schubert 88585732ac8SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 88685732ac8SCy Schubert wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 88785732ac8SCy Schubert wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 88885732ac8SCy Schubert 88985732ac8SCy Schubert /* No AES-SIV AD */ 89085732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 89185732ac8SCy Schubert if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 89285732ac8SCy Schubert wpabuf_head(clear), wpabuf_len(clear), 89385732ac8SCy Schubert 0, NULL, NULL, wrapped) < 0) 89485732ac8SCy Schubert goto fail; 89585732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 89685732ac8SCy Schubert wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); 89785732ac8SCy Schubert 89885732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 89985732ac8SCy Schubert if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { 90085732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 90185732ac8SCy Schubert dpp_build_attr_status(msg, DPP_STATUS_OK); 90285732ac8SCy Schubert } 90385732ac8SCy Schubert skip_wrapped_data: 90485732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 90585732ac8SCy Schubert 90685732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 90785732ac8SCy Schubert "DPP: Configuration Request frame attributes", msg); 908*a90b9d01SCy Schubert out: 90985732ac8SCy Schubert wpabuf_free(clear); 910*a90b9d01SCy Schubert wpabuf_free(pe); 91185732ac8SCy Schubert return msg; 91285732ac8SCy Schubert 91385732ac8SCy Schubert fail: 91485732ac8SCy Schubert wpabuf_free(msg); 915*a90b9d01SCy Schubert msg = NULL; 916*a90b9d01SCy Schubert goto out; 91785732ac8SCy Schubert } 91885732ac8SCy Schubert 91985732ac8SCy Schubert 920c1d255d3SCy Schubert void dpp_write_adv_proto(struct wpabuf *buf) 9214bc52338SCy Schubert { 9224bc52338SCy Schubert /* Advertisement Protocol IE */ 9234bc52338SCy Schubert wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); 9244bc52338SCy Schubert wpabuf_put_u8(buf, 8); /* Length */ 9254bc52338SCy Schubert wpabuf_put_u8(buf, 0x7f); 9264bc52338SCy Schubert wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 9274bc52338SCy Schubert wpabuf_put_u8(buf, 5); 9284bc52338SCy Schubert wpabuf_put_be24(buf, OUI_WFA); 9294bc52338SCy Schubert wpabuf_put_u8(buf, DPP_OUI_TYPE); 9304bc52338SCy Schubert wpabuf_put_u8(buf, 0x01); 9314bc52338SCy Schubert } 9324bc52338SCy Schubert 9334bc52338SCy Schubert 934c1d255d3SCy Schubert void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) 9354bc52338SCy Schubert { 9364bc52338SCy Schubert /* GAS Query */ 9374bc52338SCy Schubert wpabuf_put_le16(buf, wpabuf_len(query)); 9384bc52338SCy Schubert wpabuf_put_buf(buf, query); 9394bc52338SCy Schubert } 9404bc52338SCy Schubert 9414bc52338SCy Schubert 9424bc52338SCy Schubert struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, 9434bc52338SCy Schubert const char *json) 9444bc52338SCy Schubert { 9454bc52338SCy Schubert struct wpabuf *buf, *conf_req; 9464bc52338SCy Schubert 9474bc52338SCy Schubert conf_req = dpp_build_conf_req_attr(auth, json); 9484bc52338SCy Schubert if (!conf_req) { 9494bc52338SCy Schubert wpa_printf(MSG_DEBUG, 9504bc52338SCy Schubert "DPP: No configuration request data available"); 9514bc52338SCy Schubert return NULL; 9524bc52338SCy Schubert } 9534bc52338SCy Schubert 9544bc52338SCy Schubert buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); 9554bc52338SCy Schubert if (!buf) { 9564bc52338SCy Schubert wpabuf_free(conf_req); 9574bc52338SCy Schubert return NULL; 9584bc52338SCy Schubert } 9594bc52338SCy Schubert 9604bc52338SCy Schubert dpp_write_adv_proto(buf); 9614bc52338SCy Schubert dpp_write_gas_query(buf, conf_req); 9624bc52338SCy Schubert wpabuf_free(conf_req); 9634bc52338SCy Schubert wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); 9644bc52338SCy Schubert 9654bc52338SCy Schubert return buf; 9664bc52338SCy Schubert } 9674bc52338SCy Schubert 9684bc52338SCy Schubert 969c1d255d3SCy Schubert struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth, 970c1d255d3SCy Schubert const char *name, 971c1d255d3SCy Schubert enum dpp_netrole netrole, 972*a90b9d01SCy Schubert const char *mud_url, int *opclasses, 973*a90b9d01SCy Schubert const char *extra_name, 974*a90b9d01SCy Schubert const char *extra_value) 97585732ac8SCy Schubert { 976c1d255d3SCy Schubert size_t len, name_len; 977c1d255d3SCy Schubert const char *tech = "infra"; 978c1d255d3SCy Schubert const char *dpp_name; 979*a90b9d01SCy Schubert struct wpabuf *buf = NULL, *json = NULL; 980c1d255d3SCy Schubert char *csr = NULL; 98185732ac8SCy Schubert 98285732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 983c1d255d3SCy Schubert if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) { 984c1d255d3SCy Schubert static const char *bogus_tech = "knfra"; 98585732ac8SCy Schubert 986c1d255d3SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr"); 987c1d255d3SCy Schubert tech = bogus_tech; 98885732ac8SCy Schubert } 98985732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 99085732ac8SCy Schubert 991c1d255d3SCy Schubert dpp_name = name ? name : "Test"; 992c1d255d3SCy Schubert name_len = os_strlen(dpp_name); 99385732ac8SCy Schubert 994c1d255d3SCy Schubert len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4; 995c1d255d3SCy Schubert if (mud_url && mud_url[0]) 996c1d255d3SCy Schubert len += 10 + os_strlen(mud_url); 997*a90b9d01SCy Schubert if (extra_name && extra_value && extra_name[0] && extra_value[0]) 998*a90b9d01SCy Schubert len += 10 + os_strlen(extra_name) + os_strlen(extra_value); 9994bc52338SCy Schubert #ifdef CONFIG_DPP2 1000c1d255d3SCy Schubert if (auth->csr) { 1001c1d255d3SCy Schubert size_t csr_len; 100285732ac8SCy Schubert 1003c1d255d3SCy Schubert csr = base64_encode_no_lf(wpabuf_head(auth->csr), 1004c1d255d3SCy Schubert wpabuf_len(auth->csr), &csr_len); 1005c1d255d3SCy Schubert if (!csr) 1006*a90b9d01SCy Schubert goto fail; 1007c1d255d3SCy Schubert len += 30 + csr_len; 10084bc52338SCy Schubert } 10094bc52338SCy Schubert #endif /* CONFIG_DPP2 */ 1010c1d255d3SCy Schubert json = wpabuf_alloc(len); 1011c1d255d3SCy Schubert if (!json) 1012*a90b9d01SCy Schubert goto fail; 10134bc52338SCy Schubert 1014c1d255d3SCy Schubert json_start_object(json, NULL); 1015*a90b9d01SCy Schubert if (json_add_string_escape(json, "name", dpp_name, name_len) < 0) 1016*a90b9d01SCy Schubert goto fail; 1017c1d255d3SCy Schubert json_value_sep(json); 1018c1d255d3SCy Schubert json_add_string(json, "wi-fi_tech", tech); 1019c1d255d3SCy Schubert json_value_sep(json); 1020c1d255d3SCy Schubert json_add_string(json, "netRole", dpp_netrole_str(netrole)); 1021c1d255d3SCy Schubert if (mud_url && mud_url[0]) { 1022c1d255d3SCy Schubert json_value_sep(json); 1023c1d255d3SCy Schubert json_add_string(json, "mudurl", mud_url); 102485732ac8SCy Schubert } 1025c1d255d3SCy Schubert if (opclasses) { 1026c1d255d3SCy Schubert int i; 102785732ac8SCy Schubert 1028c1d255d3SCy Schubert json_value_sep(json); 1029c1d255d3SCy Schubert json_start_array(json, "bandSupport"); 1030c1d255d3SCy Schubert for (i = 0; opclasses[i]; i++) 1031c1d255d3SCy Schubert wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]); 1032c1d255d3SCy Schubert json_end_array(json); 103385732ac8SCy Schubert } 1034c1d255d3SCy Schubert if (csr) { 1035c1d255d3SCy Schubert json_value_sep(json); 1036c1d255d3SCy Schubert json_add_string(json, "pkcs10", csr); 103785732ac8SCy Schubert } 1038*a90b9d01SCy Schubert if (extra_name && extra_value && extra_name[0] && extra_value[0]) { 1039*a90b9d01SCy Schubert json_value_sep(json); 1040*a90b9d01SCy Schubert wpabuf_printf(json, "\"%s\":%s", extra_name, extra_value); 1041*a90b9d01SCy Schubert } 1042c1d255d3SCy Schubert json_end_object(json); 104385732ac8SCy Schubert 1044c1d255d3SCy Schubert buf = dpp_build_conf_req(auth, wpabuf_head(json)); 1045*a90b9d01SCy Schubert fail: 1046c1d255d3SCy Schubert wpabuf_free(json); 1047c1d255d3SCy Schubert os_free(csr); 104885732ac8SCy Schubert 1049c1d255d3SCy Schubert return buf; 105085732ac8SCy Schubert } 105185732ac8SCy Schubert 105285732ac8SCy Schubert 10534bc52338SCy Schubert static int bin_str_eq(const char *val, size_t len, const char *cmp) 10544bc52338SCy Schubert { 10554bc52338SCy Schubert return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; 10564bc52338SCy Schubert } 10574bc52338SCy Schubert 10584bc52338SCy Schubert 10594bc52338SCy Schubert struct dpp_configuration * dpp_configuration_alloc(const char *type) 10604bc52338SCy Schubert { 10614bc52338SCy Schubert struct dpp_configuration *conf; 10624bc52338SCy Schubert const char *end; 10634bc52338SCy Schubert size_t len; 10644bc52338SCy Schubert 10654bc52338SCy Schubert conf = os_zalloc(sizeof(*conf)); 10664bc52338SCy Schubert if (!conf) 10674bc52338SCy Schubert goto fail; 10684bc52338SCy Schubert 10694bc52338SCy Schubert end = os_strchr(type, ' '); 10704bc52338SCy Schubert if (end) 10714bc52338SCy Schubert len = end - type; 10724bc52338SCy Schubert else 10734bc52338SCy Schubert len = os_strlen(type); 10744bc52338SCy Schubert 10754bc52338SCy Schubert if (bin_str_eq(type, len, "psk")) 10764bc52338SCy Schubert conf->akm = DPP_AKM_PSK; 10774bc52338SCy Schubert else if (bin_str_eq(type, len, "sae")) 10784bc52338SCy Schubert conf->akm = DPP_AKM_SAE; 10794bc52338SCy Schubert else if (bin_str_eq(type, len, "psk-sae") || 10804bc52338SCy Schubert bin_str_eq(type, len, "psk+sae")) 10814bc52338SCy Schubert conf->akm = DPP_AKM_PSK_SAE; 10824bc52338SCy Schubert else if (bin_str_eq(type, len, "sae-dpp") || 10834bc52338SCy Schubert bin_str_eq(type, len, "dpp+sae")) 10844bc52338SCy Schubert conf->akm = DPP_AKM_SAE_DPP; 10854bc52338SCy Schubert else if (bin_str_eq(type, len, "psk-sae-dpp") || 10864bc52338SCy Schubert bin_str_eq(type, len, "dpp+psk+sae")) 10874bc52338SCy Schubert conf->akm = DPP_AKM_PSK_SAE_DPP; 10884bc52338SCy Schubert else if (bin_str_eq(type, len, "dpp")) 10894bc52338SCy Schubert conf->akm = DPP_AKM_DPP; 1090c1d255d3SCy Schubert else if (bin_str_eq(type, len, "dot1x")) 1091c1d255d3SCy Schubert conf->akm = DPP_AKM_DOT1X; 10924bc52338SCy Schubert else 10934bc52338SCy Schubert goto fail; 10944bc52338SCy Schubert 10954bc52338SCy Schubert return conf; 10964bc52338SCy Schubert fail: 10974bc52338SCy Schubert dpp_configuration_free(conf); 10984bc52338SCy Schubert return NULL; 10994bc52338SCy Schubert } 11004bc52338SCy Schubert 11014bc52338SCy Schubert 11024bc52338SCy Schubert int dpp_akm_psk(enum dpp_akm akm) 11034bc52338SCy Schubert { 11044bc52338SCy Schubert return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || 11054bc52338SCy Schubert akm == DPP_AKM_PSK_SAE_DPP; 11064bc52338SCy Schubert } 11074bc52338SCy Schubert 11084bc52338SCy Schubert 11094bc52338SCy Schubert int dpp_akm_sae(enum dpp_akm akm) 11104bc52338SCy Schubert { 11114bc52338SCy Schubert return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || 11124bc52338SCy Schubert akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; 11134bc52338SCy Schubert } 11144bc52338SCy Schubert 11154bc52338SCy Schubert 11164bc52338SCy Schubert int dpp_akm_legacy(enum dpp_akm akm) 11174bc52338SCy Schubert { 11184bc52338SCy Schubert return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || 11194bc52338SCy Schubert akm == DPP_AKM_SAE; 11204bc52338SCy Schubert } 11214bc52338SCy Schubert 11224bc52338SCy Schubert 11234bc52338SCy Schubert int dpp_akm_dpp(enum dpp_akm akm) 11244bc52338SCy Schubert { 11254bc52338SCy Schubert return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || 11264bc52338SCy Schubert akm == DPP_AKM_PSK_SAE_DPP; 11274bc52338SCy Schubert } 11284bc52338SCy Schubert 11294bc52338SCy Schubert 11304bc52338SCy Schubert int dpp_akm_ver2(enum dpp_akm akm) 11314bc52338SCy Schubert { 11324bc52338SCy Schubert return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; 11334bc52338SCy Schubert } 11344bc52338SCy Schubert 11354bc52338SCy Schubert 11364bc52338SCy Schubert int dpp_configuration_valid(const struct dpp_configuration *conf) 11374bc52338SCy Schubert { 11384bc52338SCy Schubert if (conf->ssid_len == 0) 11394bc52338SCy Schubert return 0; 11404bc52338SCy Schubert if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) 11414bc52338SCy Schubert return 0; 11424bc52338SCy Schubert if (dpp_akm_sae(conf->akm) && !conf->passphrase) 11434bc52338SCy Schubert return 0; 11444bc52338SCy Schubert return 1; 11454bc52338SCy Schubert } 11464bc52338SCy Schubert 11474bc52338SCy Schubert 114885732ac8SCy Schubert void dpp_configuration_free(struct dpp_configuration *conf) 114985732ac8SCy Schubert { 115085732ac8SCy Schubert if (!conf) 115185732ac8SCy Schubert return; 115285732ac8SCy Schubert str_clear_free(conf->passphrase); 115385732ac8SCy Schubert os_free(conf->group_id); 1154c1d255d3SCy Schubert os_free(conf->csrattrs); 1155*a90b9d01SCy Schubert os_free(conf->extra_name); 1156*a90b9d01SCy Schubert os_free(conf->extra_value); 115785732ac8SCy Schubert bin_clear_free(conf, sizeof(*conf)); 115885732ac8SCy Schubert } 115985732ac8SCy Schubert 116085732ac8SCy Schubert 1161c1d255d3SCy Schubert static int dpp_configuration_parse_helper(struct dpp_authentication *auth, 1162c1d255d3SCy Schubert const char *cmd, int idx) 11634bc52338SCy Schubert { 11644bc52338SCy Schubert const char *pos, *end; 11654bc52338SCy Schubert struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; 11664bc52338SCy Schubert struct dpp_configuration *conf = NULL; 1167c1d255d3SCy Schubert size_t len; 11684bc52338SCy Schubert 11694bc52338SCy Schubert pos = os_strstr(cmd, " conf=sta-"); 11704bc52338SCy Schubert if (pos) { 11714bc52338SCy Schubert conf_sta = dpp_configuration_alloc(pos + 10); 11724bc52338SCy Schubert if (!conf_sta) 11734bc52338SCy Schubert goto fail; 1174c1d255d3SCy Schubert conf_sta->netrole = DPP_NETROLE_STA; 11754bc52338SCy Schubert conf = conf_sta; 11764bc52338SCy Schubert } 11774bc52338SCy Schubert 11784bc52338SCy Schubert pos = os_strstr(cmd, " conf=ap-"); 11794bc52338SCy Schubert if (pos) { 11804bc52338SCy Schubert conf_ap = dpp_configuration_alloc(pos + 9); 11814bc52338SCy Schubert if (!conf_ap) 11824bc52338SCy Schubert goto fail; 1183c1d255d3SCy Schubert conf_ap->netrole = DPP_NETROLE_AP; 11844bc52338SCy Schubert conf = conf_ap; 11854bc52338SCy Schubert } 11864bc52338SCy Schubert 1187c1d255d3SCy Schubert pos = os_strstr(cmd, " conf=configurator"); 1188c1d255d3SCy Schubert if (pos) 1189c1d255d3SCy Schubert auth->provision_configurator = 1; 1190c1d255d3SCy Schubert 11914bc52338SCy Schubert if (!conf) 11924bc52338SCy Schubert return 0; 11934bc52338SCy Schubert 11944bc52338SCy Schubert pos = os_strstr(cmd, " ssid="); 11954bc52338SCy Schubert if (pos) { 11964bc52338SCy Schubert pos += 6; 11974bc52338SCy Schubert end = os_strchr(pos, ' '); 11984bc52338SCy Schubert conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); 11994bc52338SCy Schubert conf->ssid_len /= 2; 12004bc52338SCy Schubert if (conf->ssid_len > sizeof(conf->ssid) || 12014bc52338SCy Schubert hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) 12024bc52338SCy Schubert goto fail; 12034bc52338SCy Schubert } else { 12044bc52338SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 12054bc52338SCy Schubert /* use a default SSID for legacy testing reasons */ 12064bc52338SCy Schubert os_memcpy(conf->ssid, "test", 4); 12074bc52338SCy Schubert conf->ssid_len = 4; 12084bc52338SCy Schubert #else /* CONFIG_TESTING_OPTIONS */ 12094bc52338SCy Schubert goto fail; 12104bc52338SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 12114bc52338SCy Schubert } 12124bc52338SCy Schubert 1213c1d255d3SCy Schubert pos = os_strstr(cmd, " ssid_charset="); 1214c1d255d3SCy Schubert if (pos) { 1215c1d255d3SCy Schubert if (conf_ap) { 1216c1d255d3SCy Schubert wpa_printf(MSG_INFO, 1217c1d255d3SCy Schubert "DPP: ssid64 option (ssid_charset param) not allowed for AP enrollee"); 1218c1d255d3SCy Schubert goto fail; 1219c1d255d3SCy Schubert } 1220c1d255d3SCy Schubert conf->ssid_charset = atoi(pos + 14); 1221c1d255d3SCy Schubert } 1222c1d255d3SCy Schubert 12234bc52338SCy Schubert pos = os_strstr(cmd, " pass="); 12244bc52338SCy Schubert if (pos) { 12254bc52338SCy Schubert size_t pass_len; 12264bc52338SCy Schubert 12274bc52338SCy Schubert pos += 6; 12284bc52338SCy Schubert end = os_strchr(pos, ' '); 12294bc52338SCy Schubert pass_len = end ? (size_t) (end - pos) : os_strlen(pos); 12304bc52338SCy Schubert pass_len /= 2; 12314bc52338SCy Schubert if (pass_len > 63 || pass_len < 8) 12324bc52338SCy Schubert goto fail; 12334bc52338SCy Schubert conf->passphrase = os_zalloc(pass_len + 1); 12344bc52338SCy Schubert if (!conf->passphrase || 12354bc52338SCy Schubert hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) 12364bc52338SCy Schubert goto fail; 12374bc52338SCy Schubert } 12384bc52338SCy Schubert 12394bc52338SCy Schubert pos = os_strstr(cmd, " psk="); 12404bc52338SCy Schubert if (pos) { 12414bc52338SCy Schubert pos += 5; 12424bc52338SCy Schubert if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) 12434bc52338SCy Schubert goto fail; 12444bc52338SCy Schubert conf->psk_set = 1; 12454bc52338SCy Schubert } 12464bc52338SCy Schubert 12474bc52338SCy Schubert pos = os_strstr(cmd, " group_id="); 12484bc52338SCy Schubert if (pos) { 12494bc52338SCy Schubert size_t group_id_len; 12504bc52338SCy Schubert 12514bc52338SCy Schubert pos += 10; 12524bc52338SCy Schubert end = os_strchr(pos, ' '); 12534bc52338SCy Schubert group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); 12544bc52338SCy Schubert conf->group_id = os_malloc(group_id_len + 1); 12554bc52338SCy Schubert if (!conf->group_id) 12564bc52338SCy Schubert goto fail; 12574bc52338SCy Schubert os_memcpy(conf->group_id, pos, group_id_len); 12584bc52338SCy Schubert conf->group_id[group_id_len] = '\0'; 12594bc52338SCy Schubert } 12604bc52338SCy Schubert 12614bc52338SCy Schubert pos = os_strstr(cmd, " expiry="); 12624bc52338SCy Schubert if (pos) { 12634bc52338SCy Schubert long int val; 12644bc52338SCy Schubert 12654bc52338SCy Schubert pos += 8; 12664bc52338SCy Schubert val = strtol(pos, NULL, 0); 12674bc52338SCy Schubert if (val <= 0) 12684bc52338SCy Schubert goto fail; 12694bc52338SCy Schubert conf->netaccesskey_expiry = val; 12704bc52338SCy Schubert } 12714bc52338SCy Schubert 1272c1d255d3SCy Schubert pos = os_strstr(cmd, " csrattrs="); 1273c1d255d3SCy Schubert if (pos) { 1274c1d255d3SCy Schubert pos += 10; 1275c1d255d3SCy Schubert end = os_strchr(pos, ' '); 1276c1d255d3SCy Schubert len = end ? (size_t) (end - pos) : os_strlen(pos); 1277c1d255d3SCy Schubert conf->csrattrs = os_zalloc(len + 1); 1278c1d255d3SCy Schubert if (!conf->csrattrs) 1279c1d255d3SCy Schubert goto fail; 1280c1d255d3SCy Schubert os_memcpy(conf->csrattrs, pos, len); 1281c1d255d3SCy Schubert } 1282c1d255d3SCy Schubert 1283*a90b9d01SCy Schubert pos = os_strstr(cmd, " conf_extra_name="); 1284*a90b9d01SCy Schubert if (pos) { 1285*a90b9d01SCy Schubert pos += 17; 1286*a90b9d01SCy Schubert end = os_strchr(pos, ' '); 1287*a90b9d01SCy Schubert len = end ? (size_t) (end - pos) : os_strlen(pos); 1288*a90b9d01SCy Schubert conf->extra_name = os_zalloc(len + 1); 1289*a90b9d01SCy Schubert if (!conf->extra_name) 1290*a90b9d01SCy Schubert goto fail; 1291*a90b9d01SCy Schubert os_memcpy(conf->extra_name, pos, len); 1292*a90b9d01SCy Schubert } 1293*a90b9d01SCy Schubert 1294*a90b9d01SCy Schubert pos = os_strstr(cmd, " conf_extra_value="); 1295*a90b9d01SCy Schubert if (pos) { 1296*a90b9d01SCy Schubert pos += 18; 1297*a90b9d01SCy Schubert end = os_strchr(pos, ' '); 1298*a90b9d01SCy Schubert len = end ? (size_t) (end - pos) : os_strlen(pos); 1299*a90b9d01SCy Schubert len /= 2; 1300*a90b9d01SCy Schubert conf->extra_value = os_zalloc(len + 1); 1301*a90b9d01SCy Schubert if (!conf->extra_value || 1302*a90b9d01SCy Schubert hexstr2bin(pos, (u8 *) conf->extra_value, len) < 0) 1303*a90b9d01SCy Schubert goto fail; 1304*a90b9d01SCy Schubert } 1305*a90b9d01SCy Schubert 13064bc52338SCy Schubert if (!dpp_configuration_valid(conf)) 13074bc52338SCy Schubert goto fail; 13084bc52338SCy Schubert 1309c1d255d3SCy Schubert if (idx == 0) { 13104bc52338SCy Schubert auth->conf_sta = conf_sta; 13114bc52338SCy Schubert auth->conf_ap = conf_ap; 1312c1d255d3SCy Schubert } else if (idx == 1) { 1313*a90b9d01SCy Schubert if (!auth->conf_sta) 1314*a90b9d01SCy Schubert auth->conf_sta = conf_sta; 1315*a90b9d01SCy Schubert else 1316c1d255d3SCy Schubert auth->conf2_sta = conf_sta; 1317*a90b9d01SCy Schubert if (!auth->conf_ap) 1318*a90b9d01SCy Schubert auth->conf_ap = conf_ap; 1319*a90b9d01SCy Schubert else 1320c1d255d3SCy Schubert auth->conf2_ap = conf_ap; 1321c1d255d3SCy Schubert } else { 1322c1d255d3SCy Schubert goto fail; 1323c1d255d3SCy Schubert } 13244bc52338SCy Schubert return 0; 13254bc52338SCy Schubert 13264bc52338SCy Schubert fail: 13274bc52338SCy Schubert dpp_configuration_free(conf_sta); 13284bc52338SCy Schubert dpp_configuration_free(conf_ap); 13294bc52338SCy Schubert return -1; 13304bc52338SCy Schubert } 13314bc52338SCy Schubert 13324bc52338SCy Schubert 1333c1d255d3SCy Schubert static int dpp_configuration_parse(struct dpp_authentication *auth, 1334c1d255d3SCy Schubert const char *cmd) 1335c1d255d3SCy Schubert { 1336c1d255d3SCy Schubert const char *pos; 1337c1d255d3SCy Schubert char *tmp; 1338c1d255d3SCy Schubert size_t len; 1339c1d255d3SCy Schubert int res; 1340c1d255d3SCy Schubert 1341c1d255d3SCy Schubert pos = os_strstr(cmd, " @CONF-OBJ-SEP@ "); 1342c1d255d3SCy Schubert if (!pos) 1343c1d255d3SCy Schubert return dpp_configuration_parse_helper(auth, cmd, 0); 1344c1d255d3SCy Schubert 1345c1d255d3SCy Schubert len = pos - cmd; 1346c1d255d3SCy Schubert tmp = os_malloc(len + 1); 1347c1d255d3SCy Schubert if (!tmp) 1348c1d255d3SCy Schubert goto fail; 1349c1d255d3SCy Schubert os_memcpy(tmp, cmd, len); 1350c1d255d3SCy Schubert tmp[len] = '\0'; 1351*a90b9d01SCy Schubert res = dpp_configuration_parse_helper(auth, tmp, 0); 1352c1d255d3SCy Schubert str_clear_free(tmp); 1353c1d255d3SCy Schubert if (res) 1354c1d255d3SCy Schubert goto fail; 1355c1d255d3SCy Schubert res = dpp_configuration_parse_helper(auth, cmd + len, 1); 1356c1d255d3SCy Schubert if (res) 1357c1d255d3SCy Schubert goto fail; 1358c1d255d3SCy Schubert return 0; 1359c1d255d3SCy Schubert fail: 1360c1d255d3SCy Schubert dpp_configuration_free(auth->conf_sta); 1361c1d255d3SCy Schubert dpp_configuration_free(auth->conf2_sta); 1362c1d255d3SCy Schubert dpp_configuration_free(auth->conf_ap); 1363c1d255d3SCy Schubert dpp_configuration_free(auth->conf2_ap); 1364c1d255d3SCy Schubert return -1; 1365c1d255d3SCy Schubert } 1366c1d255d3SCy Schubert 1367c1d255d3SCy Schubert 13684bc52338SCy Schubert static struct dpp_configurator * 13694bc52338SCy Schubert dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) 13704bc52338SCy Schubert { 13714bc52338SCy Schubert struct dpp_configurator *conf; 13724bc52338SCy Schubert 13734bc52338SCy Schubert if (!dpp) 13744bc52338SCy Schubert return NULL; 13754bc52338SCy Schubert 13764bc52338SCy Schubert dl_list_for_each(conf, &dpp->configurator, 13774bc52338SCy Schubert struct dpp_configurator, list) { 13784bc52338SCy Schubert if (conf->id == id) 13794bc52338SCy Schubert return conf; 13804bc52338SCy Schubert } 13814bc52338SCy Schubert return NULL; 13824bc52338SCy Schubert } 13834bc52338SCy Schubert 13844bc52338SCy Schubert 1385c1d255d3SCy Schubert int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd) 13864bc52338SCy Schubert { 13874bc52338SCy Schubert const char *pos; 1388c1d255d3SCy Schubert char *tmp = NULL; 1389c1d255d3SCy Schubert int ret = -1; 13904bc52338SCy Schubert 1391c1d255d3SCy Schubert if (!cmd || auth->configurator_set) 13924bc52338SCy Schubert return 0; 1393c1d255d3SCy Schubert auth->configurator_set = 1; 1394c1d255d3SCy Schubert 1395c1d255d3SCy Schubert if (cmd[0] != ' ') { 1396c1d255d3SCy Schubert size_t len; 1397c1d255d3SCy Schubert 1398c1d255d3SCy Schubert len = os_strlen(cmd); 1399c1d255d3SCy Schubert tmp = os_malloc(len + 2); 1400c1d255d3SCy Schubert if (!tmp) 1401c1d255d3SCy Schubert goto fail; 1402c1d255d3SCy Schubert tmp[0] = ' '; 1403c1d255d3SCy Schubert os_memcpy(tmp + 1, cmd, len + 1); 1404c1d255d3SCy Schubert cmd = tmp; 1405c1d255d3SCy Schubert } 14064bc52338SCy Schubert 14074bc52338SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); 14084bc52338SCy Schubert 1409*a90b9d01SCy Schubert if (os_strstr(cmd, " conf=query")) { 1410*a90b9d01SCy Schubert auth->configurator_set = 0; 1411*a90b9d01SCy Schubert auth->use_config_query = true; 1412*a90b9d01SCy Schubert ret = 0; 1413*a90b9d01SCy Schubert goto fail; 1414*a90b9d01SCy Schubert } 1415*a90b9d01SCy Schubert 14164bc52338SCy Schubert pos = os_strstr(cmd, " configurator="); 1417c1d255d3SCy Schubert if (!auth->conf && pos) { 14184bc52338SCy Schubert pos += 14; 1419c1d255d3SCy Schubert auth->conf = dpp_configurator_get_id(auth->global, atoi(pos)); 14204bc52338SCy Schubert if (!auth->conf) { 14214bc52338SCy Schubert wpa_printf(MSG_INFO, 14224bc52338SCy Schubert "DPP: Could not find the specified configurator"); 1423c1d255d3SCy Schubert goto fail; 14244bc52338SCy Schubert } 14254bc52338SCy Schubert } 14264bc52338SCy Schubert 1427c1d255d3SCy Schubert pos = os_strstr(cmd, " conn_status="); 1428c1d255d3SCy Schubert if (pos) { 1429c1d255d3SCy Schubert pos += 13; 1430c1d255d3SCy Schubert auth->send_conn_status = atoi(pos); 14314bc52338SCy Schubert } 1432c1d255d3SCy Schubert 1433c1d255d3SCy Schubert pos = os_strstr(cmd, " akm_use_selector="); 1434c1d255d3SCy Schubert if (pos) { 1435c1d255d3SCy Schubert pos += 18; 1436c1d255d3SCy Schubert auth->akm_use_selector = atoi(pos); 1437c1d255d3SCy Schubert } 1438c1d255d3SCy Schubert 1439c1d255d3SCy Schubert if (dpp_configuration_parse(auth, cmd) < 0) { 1440c1d255d3SCy Schubert wpa_msg(auth->msg_ctx, MSG_INFO, 1441c1d255d3SCy Schubert "DPP: Failed to set configurator parameters"); 1442c1d255d3SCy Schubert goto fail; 1443c1d255d3SCy Schubert } 1444c1d255d3SCy Schubert ret = 0; 1445c1d255d3SCy Schubert fail: 1446c1d255d3SCy Schubert os_free(tmp); 1447c1d255d3SCy Schubert return ret; 14484bc52338SCy Schubert } 14494bc52338SCy Schubert 14504bc52338SCy Schubert 145185732ac8SCy Schubert void dpp_auth_deinit(struct dpp_authentication *auth) 145285732ac8SCy Schubert { 1453c1d255d3SCy Schubert unsigned int i; 1454c1d255d3SCy Schubert 145585732ac8SCy Schubert if (!auth) 145685732ac8SCy Schubert return; 145785732ac8SCy Schubert dpp_configuration_free(auth->conf_ap); 1458c1d255d3SCy Schubert dpp_configuration_free(auth->conf2_ap); 145985732ac8SCy Schubert dpp_configuration_free(auth->conf_sta); 1460c1d255d3SCy Schubert dpp_configuration_free(auth->conf2_sta); 14614b72b91aSCy Schubert crypto_ec_key_deinit(auth->own_protocol_key); 14624b72b91aSCy Schubert crypto_ec_key_deinit(auth->peer_protocol_key); 14634b72b91aSCy Schubert crypto_ec_key_deinit(auth->reconfig_old_protocol_key); 146485732ac8SCy Schubert wpabuf_free(auth->req_msg); 146585732ac8SCy Schubert wpabuf_free(auth->resp_msg); 146685732ac8SCy Schubert wpabuf_free(auth->conf_req); 1467c1d255d3SCy Schubert wpabuf_free(auth->reconfig_req_msg); 1468c1d255d3SCy Schubert wpabuf_free(auth->reconfig_resp_msg); 1469c1d255d3SCy Schubert for (i = 0; i < auth->num_conf_obj; i++) { 1470c1d255d3SCy Schubert struct dpp_config_obj *conf = &auth->conf_obj[i]; 1471c1d255d3SCy Schubert 1472c1d255d3SCy Schubert os_free(conf->connector); 1473c1d255d3SCy Schubert wpabuf_free(conf->c_sign_key); 1474c1d255d3SCy Schubert wpabuf_free(conf->certbag); 1475c1d255d3SCy Schubert wpabuf_free(conf->certs); 1476c1d255d3SCy Schubert wpabuf_free(conf->cacert); 1477c1d255d3SCy Schubert os_free(conf->server_name); 1478c1d255d3SCy Schubert wpabuf_free(conf->pp_key); 1479c1d255d3SCy Schubert } 1480c1d255d3SCy Schubert #ifdef CONFIG_DPP2 1481c1d255d3SCy Schubert dpp_free_asymmetric_key(auth->conf_key_pkg); 1482c1d255d3SCy Schubert os_free(auth->csrattrs); 1483c1d255d3SCy Schubert wpabuf_free(auth->csr); 1484c1d255d3SCy Schubert wpabuf_free(auth->priv_key); 1485c1d255d3SCy Schubert wpabuf_free(auth->cacert); 1486c1d255d3SCy Schubert wpabuf_free(auth->certbag); 1487c1d255d3SCy Schubert os_free(auth->trusted_eap_server_name); 1488c1d255d3SCy Schubert wpabuf_free(auth->conf_resp_tcp); 1489c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 149085732ac8SCy Schubert wpabuf_free(auth->net_access_key); 149185732ac8SCy Schubert dpp_bootstrap_info_free(auth->tmp_own_bi); 1492c1d255d3SCy Schubert if (auth->tmp_peer_bi) { 1493c1d255d3SCy Schubert dl_list_del(&auth->tmp_peer_bi->list); 1494c1d255d3SCy Schubert dpp_bootstrap_info_free(auth->tmp_peer_bi); 1495c1d255d3SCy Schubert } 1496*a90b9d01SCy Schubert os_free(auth->e_name); 1497*a90b9d01SCy Schubert os_free(auth->e_mud_url); 1498*a90b9d01SCy Schubert os_free(auth->e_band_support); 149985732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 150085732ac8SCy Schubert os_free(auth->config_obj_override); 150185732ac8SCy Schubert os_free(auth->discovery_override); 150285732ac8SCy Schubert os_free(auth->groups_override); 150385732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 150485732ac8SCy Schubert bin_clear_free(auth, sizeof(*auth)); 150585732ac8SCy Schubert } 150685732ac8SCy Schubert 150785732ac8SCy Schubert 150885732ac8SCy Schubert static struct wpabuf * 150985732ac8SCy Schubert dpp_build_conf_start(struct dpp_authentication *auth, 151085732ac8SCy Schubert struct dpp_configuration *conf, size_t tailroom) 151185732ac8SCy Schubert { 151285732ac8SCy Schubert struct wpabuf *buf; 151385732ac8SCy Schubert 151485732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 151585732ac8SCy Schubert if (auth->discovery_override) 151685732ac8SCy Schubert tailroom += os_strlen(auth->discovery_override); 151785732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 151885732ac8SCy Schubert 151985732ac8SCy Schubert buf = wpabuf_alloc(200 + tailroom); 152085732ac8SCy Schubert if (!buf) 152185732ac8SCy Schubert return NULL; 1522c1d255d3SCy Schubert json_start_object(buf, NULL); 1523c1d255d3SCy Schubert json_add_string(buf, "wi-fi_tech", "infra"); 1524c1d255d3SCy Schubert json_value_sep(buf); 152585732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 152685732ac8SCy Schubert if (auth->discovery_override) { 152785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", 152885732ac8SCy Schubert auth->discovery_override); 1529c1d255d3SCy Schubert wpabuf_put_str(buf, "\"discovery\":"); 153085732ac8SCy Schubert wpabuf_put_str(buf, auth->discovery_override); 1531c1d255d3SCy Schubert json_value_sep(buf); 153285732ac8SCy Schubert return buf; 153385732ac8SCy Schubert } 153485732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 1535c1d255d3SCy Schubert json_start_object(buf, "discovery"); 1536c1d255d3SCy Schubert if (((!conf->ssid_charset || auth->peer_version < 2) && 1537c1d255d3SCy Schubert json_add_string_escape(buf, "ssid", conf->ssid, 1538c1d255d3SCy Schubert conf->ssid_len) < 0) || 1539c1d255d3SCy Schubert ((conf->ssid_charset && auth->peer_version >= 2) && 1540c1d255d3SCy Schubert json_add_base64url(buf, "ssid64", conf->ssid, 1541c1d255d3SCy Schubert conf->ssid_len) < 0)) { 1542c1d255d3SCy Schubert wpabuf_free(buf); 1543c1d255d3SCy Schubert return NULL; 1544c1d255d3SCy Schubert } 1545c1d255d3SCy Schubert if (conf->ssid_charset > 0) { 1546c1d255d3SCy Schubert json_value_sep(buf); 1547c1d255d3SCy Schubert json_add_int(buf, "ssid_charset", conf->ssid_charset); 1548c1d255d3SCy Schubert } 1549c1d255d3SCy Schubert json_end_object(buf); 1550c1d255d3SCy Schubert json_value_sep(buf); 155185732ac8SCy Schubert 155285732ac8SCy Schubert return buf; 155385732ac8SCy Schubert } 155485732ac8SCy Schubert 155585732ac8SCy Schubert 15564b72b91aSCy Schubert int dpp_build_jwk(struct wpabuf *buf, const char *name, 15574b72b91aSCy Schubert struct crypto_ec_key *key, const char *kid, 15584b72b91aSCy Schubert const struct dpp_curve_params *curve) 155985732ac8SCy Schubert { 156085732ac8SCy Schubert struct wpabuf *pub; 156185732ac8SCy Schubert const u8 *pos; 156285732ac8SCy Schubert int ret = -1; 156385732ac8SCy Schubert 15644b72b91aSCy Schubert pub = crypto_ec_key_get_pubkey_point(key, 0); 156585732ac8SCy Schubert if (!pub) 156685732ac8SCy Schubert goto fail; 156785732ac8SCy Schubert 1568c1d255d3SCy Schubert json_start_object(buf, name); 1569c1d255d3SCy Schubert json_add_string(buf, "kty", "EC"); 1570c1d255d3SCy Schubert json_value_sep(buf); 1571c1d255d3SCy Schubert json_add_string(buf, "crv", curve->jwk_crv); 1572c1d255d3SCy Schubert json_value_sep(buf); 1573c1d255d3SCy Schubert pos = wpabuf_head(pub); 1574c1d255d3SCy Schubert if (json_add_base64url(buf, "x", pos, curve->prime_len) < 0) 1575c1d255d3SCy Schubert goto fail; 1576c1d255d3SCy Schubert json_value_sep(buf); 1577c1d255d3SCy Schubert pos += curve->prime_len; 1578c1d255d3SCy Schubert if (json_add_base64url(buf, "y", pos, curve->prime_len) < 0) 1579c1d255d3SCy Schubert goto fail; 158085732ac8SCy Schubert if (kid) { 1581c1d255d3SCy Schubert json_value_sep(buf); 1582c1d255d3SCy Schubert json_add_string(buf, "kid", kid); 158385732ac8SCy Schubert } 1584c1d255d3SCy Schubert json_end_object(buf); 158585732ac8SCy Schubert ret = 0; 158685732ac8SCy Schubert fail: 158785732ac8SCy Schubert wpabuf_free(pub); 158885732ac8SCy Schubert return ret; 158985732ac8SCy Schubert } 159085732ac8SCy Schubert 159185732ac8SCy Schubert 15924bc52338SCy Schubert static void dpp_build_legacy_cred_params(struct wpabuf *buf, 15934bc52338SCy Schubert struct dpp_configuration *conf) 15944bc52338SCy Schubert { 15954bc52338SCy Schubert if (conf->passphrase && os_strlen(conf->passphrase) < 64) { 1596c1d255d3SCy Schubert json_add_string_escape(buf, "pass", conf->passphrase, 15974bc52338SCy Schubert os_strlen(conf->passphrase)); 15984bc52338SCy Schubert } else if (conf->psk_set) { 15994bc52338SCy Schubert char psk[2 * sizeof(conf->psk) + 1]; 16004bc52338SCy Schubert 16014bc52338SCy Schubert wpa_snprintf_hex(psk, sizeof(psk), 16024bc52338SCy Schubert conf->psk, sizeof(conf->psk)); 1603c1d255d3SCy Schubert json_add_string(buf, "psk_hex", psk); 1604c1d255d3SCy Schubert forced_memzero(psk, sizeof(psk)); 1605c1d255d3SCy Schubert } 1606c1d255d3SCy Schubert } 1607c1d255d3SCy Schubert 1608c1d255d3SCy Schubert 1609*a90b9d01SCy Schubert const char * dpp_netrole_str(enum dpp_netrole netrole) 1610c1d255d3SCy Schubert { 1611c1d255d3SCy Schubert switch (netrole) { 1612c1d255d3SCy Schubert case DPP_NETROLE_STA: 1613c1d255d3SCy Schubert return "sta"; 1614c1d255d3SCy Schubert case DPP_NETROLE_AP: 1615c1d255d3SCy Schubert return "ap"; 1616c1d255d3SCy Schubert case DPP_NETROLE_CONFIGURATOR: 1617c1d255d3SCy Schubert return "configurator"; 1618c1d255d3SCy Schubert default: 1619c1d255d3SCy Schubert return "??"; 16204bc52338SCy Schubert } 16214bc52338SCy Schubert } 16224bc52338SCy Schubert 16234bc52338SCy Schubert 1624*a90b9d01SCy Schubert static bool dpp_supports_curve(const char *curve, struct dpp_bootstrap_info *bi) 1625*a90b9d01SCy Schubert { 1626*a90b9d01SCy Schubert enum dpp_bootstrap_supported_curves idx; 1627*a90b9d01SCy Schubert 1628*a90b9d01SCy Schubert if (!bi || !bi->supported_curves) 1629*a90b9d01SCy Schubert return true; /* no support indication available */ 1630*a90b9d01SCy Schubert 1631*a90b9d01SCy Schubert if (os_strcmp(curve, "prime256v1") == 0) 1632*a90b9d01SCy Schubert idx = DPP_BOOTSTRAP_CURVE_P_256; 1633*a90b9d01SCy Schubert else if (os_strcmp(curve, "secp384r1") == 0) 1634*a90b9d01SCy Schubert idx = DPP_BOOTSTRAP_CURVE_P_384; 1635*a90b9d01SCy Schubert else if (os_strcmp(curve, "secp521r1") == 0) 1636*a90b9d01SCy Schubert idx = DPP_BOOTSTRAP_CURVE_P_521; 1637*a90b9d01SCy Schubert else if (os_strcmp(curve, "brainpoolP256r1") == 0) 1638*a90b9d01SCy Schubert idx = DPP_BOOTSTRAP_CURVE_BP_256; 1639*a90b9d01SCy Schubert else if (os_strcmp(curve, "brainpoolP384r1") == 0) 1640*a90b9d01SCy Schubert idx = DPP_BOOTSTRAP_CURVE_BP_384; 1641*a90b9d01SCy Schubert else if (os_strcmp(curve, "brainpoolP512r1") == 0) 1642*a90b9d01SCy Schubert idx = DPP_BOOTSTRAP_CURVE_BP_512; 1643*a90b9d01SCy Schubert else 1644*a90b9d01SCy Schubert return true; 1645*a90b9d01SCy Schubert 1646*a90b9d01SCy Schubert return bi->supported_curves & BIT(idx); 1647*a90b9d01SCy Schubert } 1648*a90b9d01SCy Schubert 1649*a90b9d01SCy Schubert 165085732ac8SCy Schubert static struct wpabuf * 1651c1d255d3SCy Schubert dpp_build_conf_obj_dpp(struct dpp_authentication *auth, 165285732ac8SCy Schubert struct dpp_configuration *conf) 165385732ac8SCy Schubert { 165485732ac8SCy Schubert struct wpabuf *buf = NULL; 1655c1d255d3SCy Schubert char *signed_conn = NULL; 165685732ac8SCy Schubert size_t tailroom; 1657*a90b9d01SCy Schubert const struct dpp_curve_params *curve; /* C-sign-key curve */ 1658*a90b9d01SCy Schubert const struct dpp_curve_params *nak_curve; /* netAccessKey curve */ 165985732ac8SCy Schubert struct wpabuf *dppcon = NULL; 166085732ac8SCy Schubert size_t extra_len = 1000; 16614bc52338SCy Schubert int incl_legacy; 16624bc52338SCy Schubert enum dpp_akm akm; 1663c1d255d3SCy Schubert const char *akm_str; 166485732ac8SCy Schubert 166585732ac8SCy Schubert if (!auth->conf) { 166685732ac8SCy Schubert wpa_printf(MSG_INFO, 166785732ac8SCy Schubert "DPP: No configurator specified - cannot generate DPP config object"); 166885732ac8SCy Schubert goto fail; 166985732ac8SCy Schubert } 167085732ac8SCy Schubert curve = auth->conf->curve; 1671*a90b9d01SCy Schubert if (dpp_akm_dpp(conf->akm) && 1672*a90b9d01SCy Schubert !dpp_supports_curve(curve->name, auth->peer_bi)) { 1673*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1674*a90b9d01SCy Schubert "DPP: Enrollee does not support C-sign-key curve (%s) - cannot generate config object", 1675*a90b9d01SCy Schubert curve->name); 1676*a90b9d01SCy Schubert goto fail; 1677*a90b9d01SCy Schubert } 1678*a90b9d01SCy Schubert if (auth->new_curve && auth->new_key_received) 1679*a90b9d01SCy Schubert nak_curve = auth->new_curve; 1680*a90b9d01SCy Schubert else 1681*a90b9d01SCy Schubert nak_curve = auth->curve; 1682*a90b9d01SCy Schubert if (!dpp_supports_curve(nak_curve->name, auth->peer_bi)) { 1683*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1684*a90b9d01SCy Schubert "DPP: Enrollee does not support netAccessKey curve (%s) - cannot generate config object", 1685*a90b9d01SCy Schubert nak_curve->name); 1686*a90b9d01SCy Schubert goto fail; 1687*a90b9d01SCy Schubert } 168885732ac8SCy Schubert 16894bc52338SCy Schubert akm = conf->akm; 16904bc52338SCy Schubert if (dpp_akm_ver2(akm) && auth->peer_version < 2) { 16914bc52338SCy Schubert wpa_printf(MSG_DEBUG, 16924bc52338SCy Schubert "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); 16934bc52338SCy Schubert akm = DPP_AKM_DPP; 16944bc52338SCy Schubert } 16954bc52338SCy Schubert 169685732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 169785732ac8SCy Schubert if (auth->groups_override) 169885732ac8SCy Schubert extra_len += os_strlen(auth->groups_override); 169985732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 170085732ac8SCy Schubert 170185732ac8SCy Schubert if (conf->group_id) 170285732ac8SCy Schubert extra_len += os_strlen(conf->group_id); 170385732ac8SCy Schubert 170485732ac8SCy Schubert /* Connector (JSON dppCon object) */ 1705*a90b9d01SCy Schubert dppcon = wpabuf_alloc(extra_len + 2 * nak_curve->prime_len * 4 / 3); 170685732ac8SCy Schubert if (!dppcon) 170785732ac8SCy Schubert goto fail; 170885732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 170985732ac8SCy Schubert if (auth->groups_override) { 171085732ac8SCy Schubert wpabuf_put_u8(dppcon, '{'); 171185732ac8SCy Schubert if (auth->groups_override) { 171285732ac8SCy Schubert wpa_printf(MSG_DEBUG, 171385732ac8SCy Schubert "DPP: TESTING - groups override: '%s'", 171485732ac8SCy Schubert auth->groups_override); 171585732ac8SCy Schubert wpabuf_put_str(dppcon, "\"groups\":"); 171685732ac8SCy Schubert wpabuf_put_str(dppcon, auth->groups_override); 1717c1d255d3SCy Schubert json_value_sep(dppcon); 171885732ac8SCy Schubert } 171985732ac8SCy Schubert goto skip_groups; 172085732ac8SCy Schubert } 172185732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 1722c1d255d3SCy Schubert json_start_object(dppcon, NULL); 1723c1d255d3SCy Schubert json_start_array(dppcon, "groups"); 1724c1d255d3SCy Schubert json_start_object(dppcon, NULL); 1725c1d255d3SCy Schubert json_add_string(dppcon, "groupId", 172685732ac8SCy Schubert conf->group_id ? conf->group_id : "*"); 1727c1d255d3SCy Schubert json_value_sep(dppcon); 1728c1d255d3SCy Schubert json_add_string(dppcon, "netRole", dpp_netrole_str(conf->netrole)); 1729c1d255d3SCy Schubert json_end_object(dppcon); 1730c1d255d3SCy Schubert json_end_array(dppcon); 1731c1d255d3SCy Schubert json_value_sep(dppcon); 173285732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 173385732ac8SCy Schubert skip_groups: 173485732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 1735*a90b9d01SCy Schubert if (!auth->peer_protocol_key) { 1736*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1737*a90b9d01SCy Schubert "DPP: No peer protocol key available to build netAccessKey JWK"); 1738*a90b9d01SCy Schubert goto fail; 1739*a90b9d01SCy Schubert } 1740*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 1741*a90b9d01SCy Schubert if (auth->conf->net_access_key_curve && 1742*a90b9d01SCy Schubert auth->curve != auth->conf->net_access_key_curve && 1743*a90b9d01SCy Schubert !auth->new_key_received) { 1744*a90b9d01SCy Schubert if (!dpp_supports_curve(auth->conf->net_access_key_curve->name, 1745*a90b9d01SCy Schubert auth->peer_bi)) { 1746*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1747*a90b9d01SCy Schubert "DPP: Enrollee does not support the required netAccessKey curve (%s) - cannot generate config object", 1748*a90b9d01SCy Schubert auth->conf->net_access_key_curve->name); 1749*a90b9d01SCy Schubert goto fail; 1750*a90b9d01SCy Schubert } 1751*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1752*a90b9d01SCy Schubert "DPP: Peer protocol key curve (%s) does not match the required netAccessKey curve (%s) - %s", 1753*a90b9d01SCy Schubert auth->curve->name, 1754*a90b9d01SCy Schubert auth->conf->net_access_key_curve->name, 1755*a90b9d01SCy Schubert auth->waiting_new_key ? 1756*a90b9d01SCy Schubert "the required key not received" : 1757*a90b9d01SCy Schubert "request a new key"); 1758*a90b9d01SCy Schubert if (auth->waiting_new_key) 1759*a90b9d01SCy Schubert auth->waiting_new_key = false; /* failed */ 1760*a90b9d01SCy Schubert else 1761*a90b9d01SCy Schubert auth->waiting_new_key = true; 1762*a90b9d01SCy Schubert goto fail; 1763*a90b9d01SCy Schubert } 1764*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 1765*a90b9d01SCy Schubert if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, 1766*a90b9d01SCy Schubert nak_curve) < 0) { 176785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); 176885732ac8SCy Schubert goto fail; 176985732ac8SCy Schubert } 177085732ac8SCy Schubert if (conf->netaccesskey_expiry) { 177185732ac8SCy Schubert struct os_tm tm; 1772c1d255d3SCy Schubert char expiry[30]; 177385732ac8SCy Schubert 177485732ac8SCy Schubert if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { 177585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 177685732ac8SCy Schubert "DPP: Failed to generate expiry string"); 177785732ac8SCy Schubert goto fail; 177885732ac8SCy Schubert } 1779c1d255d3SCy Schubert os_snprintf(expiry, sizeof(expiry), 1780c1d255d3SCy Schubert "%04u-%02u-%02uT%02u:%02u:%02uZ", 178185732ac8SCy Schubert tm.year, tm.month, tm.day, 178285732ac8SCy Schubert tm.hour, tm.min, tm.sec); 1783c1d255d3SCy Schubert json_value_sep(dppcon); 1784c1d255d3SCy Schubert json_add_string(dppcon, "expiry", expiry); 178585732ac8SCy Schubert } 178632a95656SCy Schubert #ifdef CONFIG_DPP3 178732a95656SCy Schubert json_value_sep(dppcon); 178832a95656SCy Schubert json_add_int(dppcon, "version", auth->peer_version); 178932a95656SCy Schubert #endif /* CONFIG_DPP3 */ 1790c1d255d3SCy Schubert json_end_object(dppcon); 179185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", 179285732ac8SCy Schubert (const char *) wpabuf_head(dppcon)); 179385732ac8SCy Schubert 1794c1d255d3SCy Schubert signed_conn = dpp_sign_connector(auth->conf, dppcon); 1795c1d255d3SCy Schubert if (!signed_conn) 179685732ac8SCy Schubert goto fail; 179785732ac8SCy Schubert 17984bc52338SCy Schubert incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); 179985732ac8SCy Schubert tailroom = 1000; 180085732ac8SCy Schubert tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); 1801c1d255d3SCy Schubert tailroom += os_strlen(signed_conn); 18024bc52338SCy Schubert if (incl_legacy) 18034bc52338SCy Schubert tailroom += 1000; 1804c1d255d3SCy Schubert if (akm == DPP_AKM_DOT1X) { 1805c1d255d3SCy Schubert if (auth->certbag) 1806c1d255d3SCy Schubert tailroom += 2 * wpabuf_len(auth->certbag); 1807c1d255d3SCy Schubert if (auth->cacert) 1808c1d255d3SCy Schubert tailroom += 2 * wpabuf_len(auth->cacert); 1809c1d255d3SCy Schubert if (auth->trusted_eap_server_name) 1810c1d255d3SCy Schubert tailroom += os_strlen(auth->trusted_eap_server_name); 1811c1d255d3SCy Schubert tailroom += 1000; 1812c1d255d3SCy Schubert } 1813*a90b9d01SCy Schubert if (conf->extra_name && conf->extra_value) 1814*a90b9d01SCy Schubert tailroom += 10 + os_strlen(conf->extra_name) + 1815*a90b9d01SCy Schubert os_strlen(conf->extra_value); 181685732ac8SCy Schubert buf = dpp_build_conf_start(auth, conf, tailroom); 181785732ac8SCy Schubert if (!buf) 181885732ac8SCy Schubert goto fail; 181985732ac8SCy Schubert 1820c1d255d3SCy Schubert if (auth->akm_use_selector && dpp_akm_ver2(akm)) 1821c1d255d3SCy Schubert akm_str = dpp_akm_selector_str(akm); 1822c1d255d3SCy Schubert else 1823c1d255d3SCy Schubert akm_str = dpp_akm_str(akm); 1824c1d255d3SCy Schubert json_start_object(buf, "cred"); 1825c1d255d3SCy Schubert json_add_string(buf, "akm", akm_str); 1826c1d255d3SCy Schubert json_value_sep(buf); 18274bc52338SCy Schubert if (incl_legacy) { 18284bc52338SCy Schubert dpp_build_legacy_cred_params(buf, conf); 1829c1d255d3SCy Schubert json_value_sep(buf); 1830c1d255d3SCy Schubert } 1831c1d255d3SCy Schubert if (akm == DPP_AKM_DOT1X) { 1832c1d255d3SCy Schubert json_start_object(buf, "entCreds"); 1833c1d255d3SCy Schubert if (!auth->certbag) 1834c1d255d3SCy Schubert goto fail; 1835c1d255d3SCy Schubert json_add_base64(buf, "certBag", wpabuf_head(auth->certbag), 1836c1d255d3SCy Schubert wpabuf_len(auth->certbag)); 1837c1d255d3SCy Schubert if (auth->cacert) { 1838c1d255d3SCy Schubert json_value_sep(buf); 1839c1d255d3SCy Schubert json_add_base64(buf, "caCert", 1840c1d255d3SCy Schubert wpabuf_head(auth->cacert), 1841c1d255d3SCy Schubert wpabuf_len(auth->cacert)); 1842c1d255d3SCy Schubert } 1843c1d255d3SCy Schubert if (auth->trusted_eap_server_name) { 1844c1d255d3SCy Schubert json_value_sep(buf); 1845c1d255d3SCy Schubert json_add_string(buf, "trustedEapServerName", 1846c1d255d3SCy Schubert auth->trusted_eap_server_name); 1847c1d255d3SCy Schubert } 1848c1d255d3SCy Schubert json_value_sep(buf); 1849c1d255d3SCy Schubert json_start_array(buf, "eapMethods"); 1850c1d255d3SCy Schubert wpabuf_printf(buf, "%d", EAP_TYPE_TLS); 1851c1d255d3SCy Schubert json_end_array(buf); 1852c1d255d3SCy Schubert json_end_object(buf); 1853c1d255d3SCy Schubert json_value_sep(buf); 18544bc52338SCy Schubert } 18554bc52338SCy Schubert wpabuf_put_str(buf, "\"signedConnector\":\""); 1856c1d255d3SCy Schubert wpabuf_put_str(buf, signed_conn); 1857c1d255d3SCy Schubert wpabuf_put_str(buf, "\""); 1858c1d255d3SCy Schubert json_value_sep(buf); 185985732ac8SCy Schubert if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, 186085732ac8SCy Schubert curve) < 0) { 186185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); 186285732ac8SCy Schubert goto fail; 186385732ac8SCy Schubert } 1864c1d255d3SCy Schubert #ifdef CONFIG_DPP2 1865c1d255d3SCy Schubert if (auth->peer_version >= 2 && auth->conf->pp_key) { 1866c1d255d3SCy Schubert json_value_sep(buf); 1867c1d255d3SCy Schubert if (dpp_build_jwk(buf, "ppKey", auth->conf->pp_key, NULL, 1868c1d255d3SCy Schubert curve) < 0) { 1869c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to build ppKey JWK"); 1870c1d255d3SCy Schubert goto fail; 1871c1d255d3SCy Schubert } 1872c1d255d3SCy Schubert } 1873c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 187485732ac8SCy Schubert 1875c1d255d3SCy Schubert json_end_object(buf); 1876*a90b9d01SCy Schubert if (conf->extra_name && conf->extra_value) { 1877*a90b9d01SCy Schubert json_value_sep(buf); 1878*a90b9d01SCy Schubert wpabuf_printf(buf, "\"%s\":%s", conf->extra_name, 1879*a90b9d01SCy Schubert conf->extra_value); 1880*a90b9d01SCy Schubert } 1881c1d255d3SCy Schubert json_end_object(buf); 188285732ac8SCy Schubert 188385732ac8SCy Schubert wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", 188485732ac8SCy Schubert wpabuf_head(buf), wpabuf_len(buf)); 188585732ac8SCy Schubert 1886*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 1887*a90b9d01SCy Schubert if (!auth->conf->net_access_key_curve) { 1888*a90b9d01SCy Schubert /* All netAccessKey values used in the network will have to be 1889*a90b9d01SCy Schubert * from the same curve for network introduction to work, so 1890*a90b9d01SCy Schubert * hardcode the first used netAccessKey curve for consecutive 1891*a90b9d01SCy Schubert * operations if there was no explicit configuration of which 1892*a90b9d01SCy Schubert * curve to use. */ 1893*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 1894*a90b9d01SCy Schubert "DPP: Update Configurator to require netAccessKey curve %s based on first provisioning", 1895*a90b9d01SCy Schubert nak_curve->name); 1896*a90b9d01SCy Schubert auth->conf->net_access_key_curve = nak_curve; 1897*a90b9d01SCy Schubert } 1898*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 1899*a90b9d01SCy Schubert 190085732ac8SCy Schubert out: 1901c1d255d3SCy Schubert os_free(signed_conn); 190285732ac8SCy Schubert wpabuf_free(dppcon); 190385732ac8SCy Schubert return buf; 190485732ac8SCy Schubert fail: 190585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); 190685732ac8SCy Schubert wpabuf_free(buf); 190785732ac8SCy Schubert buf = NULL; 190885732ac8SCy Schubert goto out; 190985732ac8SCy Schubert } 191085732ac8SCy Schubert 191185732ac8SCy Schubert 191285732ac8SCy Schubert static struct wpabuf * 1913c1d255d3SCy Schubert dpp_build_conf_obj_legacy(struct dpp_authentication *auth, 191485732ac8SCy Schubert struct dpp_configuration *conf) 191585732ac8SCy Schubert { 191685732ac8SCy Schubert struct wpabuf *buf; 1917c1d255d3SCy Schubert const char *akm_str; 1918*a90b9d01SCy Schubert size_t len = 1000; 191985732ac8SCy Schubert 1920*a90b9d01SCy Schubert if (conf->extra_name && conf->extra_value) 1921*a90b9d01SCy Schubert len += 10 + os_strlen(conf->extra_name) + 1922*a90b9d01SCy Schubert os_strlen(conf->extra_value); 1923*a90b9d01SCy Schubert buf = dpp_build_conf_start(auth, conf, len); 192485732ac8SCy Schubert if (!buf) 192585732ac8SCy Schubert return NULL; 192685732ac8SCy Schubert 1927c1d255d3SCy Schubert if (auth->akm_use_selector && dpp_akm_ver2(conf->akm)) 1928c1d255d3SCy Schubert akm_str = dpp_akm_selector_str(conf->akm); 1929c1d255d3SCy Schubert else 1930c1d255d3SCy Schubert akm_str = dpp_akm_str(conf->akm); 1931c1d255d3SCy Schubert json_start_object(buf, "cred"); 1932c1d255d3SCy Schubert json_add_string(buf, "akm", akm_str); 1933c1d255d3SCy Schubert json_value_sep(buf); 19344bc52338SCy Schubert dpp_build_legacy_cred_params(buf, conf); 1935c1d255d3SCy Schubert json_end_object(buf); 1936*a90b9d01SCy Schubert if (conf->extra_name && conf->extra_value) { 1937*a90b9d01SCy Schubert json_value_sep(buf); 1938*a90b9d01SCy Schubert wpabuf_printf(buf, "\"%s\":%s", conf->extra_name, 1939*a90b9d01SCy Schubert conf->extra_value); 1940*a90b9d01SCy Schubert } 1941c1d255d3SCy Schubert json_end_object(buf); 194285732ac8SCy Schubert 194385732ac8SCy Schubert wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", 194485732ac8SCy Schubert wpabuf_head(buf), wpabuf_len(buf)); 194585732ac8SCy Schubert 194685732ac8SCy Schubert return buf; 194785732ac8SCy Schubert } 194885732ac8SCy Schubert 194985732ac8SCy Schubert 1950*a90b9d01SCy Schubert static int dpp_get_peer_bi_id(struct dpp_authentication *auth) 1951*a90b9d01SCy Schubert { 1952*a90b9d01SCy Schubert struct dpp_bootstrap_info *bi; 1953*a90b9d01SCy Schubert 1954*a90b9d01SCy Schubert if (auth->peer_bi) 1955*a90b9d01SCy Schubert return auth->peer_bi->id; 1956*a90b9d01SCy Schubert if (auth->tmp_peer_bi) 1957*a90b9d01SCy Schubert return auth->tmp_peer_bi->id; 1958*a90b9d01SCy Schubert 1959*a90b9d01SCy Schubert bi = os_zalloc(sizeof(*bi)); 1960*a90b9d01SCy Schubert if (!bi) 1961*a90b9d01SCy Schubert return -1; 1962*a90b9d01SCy Schubert bi->id = dpp_next_id(auth->global); 1963*a90b9d01SCy Schubert dl_list_add(&auth->global->bootstrap, &bi->list); 1964*a90b9d01SCy Schubert auth->tmp_peer_bi = bi; 1965*a90b9d01SCy Schubert return bi->id; 1966*a90b9d01SCy Schubert } 1967*a90b9d01SCy Schubert 1968*a90b9d01SCy Schubert 196985732ac8SCy Schubert static struct wpabuf * 1970c1d255d3SCy Schubert dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole, 1971c1d255d3SCy Schubert int idx, bool cert_req) 197285732ac8SCy Schubert { 1973c1d255d3SCy Schubert struct dpp_configuration *conf = NULL; 197485732ac8SCy Schubert 197585732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 197685732ac8SCy Schubert if (auth->config_obj_override) { 1977c1d255d3SCy Schubert if (idx != 0) 1978c1d255d3SCy Schubert return NULL; 197985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); 198085732ac8SCy Schubert return wpabuf_alloc_copy(auth->config_obj_override, 198185732ac8SCy Schubert os_strlen(auth->config_obj_override)); 198285732ac8SCy Schubert } 198385732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 198485732ac8SCy Schubert 1985c1d255d3SCy Schubert if (idx == 0) { 1986c1d255d3SCy Schubert if (netrole == DPP_NETROLE_STA) 1987c1d255d3SCy Schubert conf = auth->conf_sta; 1988c1d255d3SCy Schubert else if (netrole == DPP_NETROLE_AP) 1989c1d255d3SCy Schubert conf = auth->conf_ap; 1990c1d255d3SCy Schubert } else if (idx == 1) { 1991c1d255d3SCy Schubert if (netrole == DPP_NETROLE_STA) 1992c1d255d3SCy Schubert conf = auth->conf2_sta; 1993c1d255d3SCy Schubert else if (netrole == DPP_NETROLE_AP) 1994c1d255d3SCy Schubert conf = auth->conf2_ap; 1995c1d255d3SCy Schubert } 199685732ac8SCy Schubert if (!conf) { 1997*a90b9d01SCy Schubert if (idx == 0) { 1998*a90b9d01SCy Schubert if (auth->use_config_query) { 1999*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 2000*a90b9d01SCy Schubert "DPP: No configuration available for Enrollee(%s) - waiting for configuration", 2001*a90b9d01SCy Schubert dpp_netrole_str(netrole)); 2002*a90b9d01SCy Schubert auth->waiting_config = true; 2003*a90b9d01SCy Schubert dpp_get_peer_bi_id(auth); 2004*a90b9d01SCy Schubert return NULL; 2005*a90b9d01SCy Schubert } 200685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 200785732ac8SCy Schubert "DPP: No configuration available for Enrollee(%s) - reject configuration request", 2008c1d255d3SCy Schubert dpp_netrole_str(netrole)); 2009*a90b9d01SCy Schubert } 201085732ac8SCy Schubert return NULL; 201185732ac8SCy Schubert } 201285732ac8SCy Schubert 2013c1d255d3SCy Schubert if (conf->akm == DPP_AKM_DOT1X) { 2014c1d255d3SCy Schubert if (!auth->conf) { 2015c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2016c1d255d3SCy Schubert "DPP: No Configurator data available"); 2017c1d255d3SCy Schubert return NULL; 2018c1d255d3SCy Schubert } 2019c1d255d3SCy Schubert if (!cert_req && !auth->certbag) { 2020c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2021c1d255d3SCy Schubert "DPP: No certificate data available for dot1x configuration"); 2022c1d255d3SCy Schubert return NULL; 2023c1d255d3SCy Schubert } 2024c1d255d3SCy Schubert return dpp_build_conf_obj_dpp(auth, conf); 2025c1d255d3SCy Schubert } 2026c1d255d3SCy Schubert if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf)) 2027c1d255d3SCy Schubert return dpp_build_conf_obj_dpp(auth, conf); 2028c1d255d3SCy Schubert return dpp_build_conf_obj_legacy(auth, conf); 202985732ac8SCy Schubert } 203085732ac8SCy Schubert 203185732ac8SCy Schubert 2032c1d255d3SCy Schubert struct wpabuf * 203385732ac8SCy Schubert dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, 2034c1d255d3SCy Schubert u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req) 203585732ac8SCy Schubert { 2036*a90b9d01SCy Schubert struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL, *pc = NULL; 203785732ac8SCy Schubert size_t clear_len, attr_len; 203885732ac8SCy Schubert struct wpabuf *clear = NULL, *msg = NULL; 203985732ac8SCy Schubert u8 *wrapped; 204085732ac8SCy Schubert const u8 *addr[1]; 204185732ac8SCy Schubert size_t len[1]; 204285732ac8SCy Schubert enum dpp_status_error status; 204385732ac8SCy Schubert 2044c1d255d3SCy Schubert if (auth->force_conf_resp_status != DPP_STATUS_OK) { 2045c1d255d3SCy Schubert status = auth->force_conf_resp_status; 2046c1d255d3SCy Schubert goto forced_status; 204785732ac8SCy Schubert } 2048c1d255d3SCy Schubert 2049c1d255d3SCy Schubert if (netrole == DPP_NETROLE_CONFIGURATOR) { 2050c1d255d3SCy Schubert #ifdef CONFIG_DPP2 2051c1d255d3SCy Schubert env_data = dpp_build_enveloped_data(auth); 2052c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 2053c1d255d3SCy Schubert } else { 2054c1d255d3SCy Schubert conf = dpp_build_conf_obj(auth, netrole, 0, cert_req); 2055c1d255d3SCy Schubert if (conf) { 2056c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, 2057c1d255d3SCy Schubert "DPP: configurationObject JSON", 2058c1d255d3SCy Schubert wpabuf_head(conf), wpabuf_len(conf)); 2059c1d255d3SCy Schubert conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req); 2060c1d255d3SCy Schubert } 2061c1d255d3SCy Schubert } 2062c1d255d3SCy Schubert 2063*a90b9d01SCy Schubert if (!conf && auth->waiting_config) 2064*a90b9d01SCy Schubert return NULL; 2065c1d255d3SCy Schubert if (conf || env_data) 2066c1d255d3SCy Schubert status = DPP_STATUS_OK; 2067c1d255d3SCy Schubert else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta && 2068c1d255d3SCy Schubert auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr) 2069c1d255d3SCy Schubert status = DPP_STATUS_CSR_NEEDED; 2070*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 2071*a90b9d01SCy Schubert else if (auth->waiting_new_key) 2072*a90b9d01SCy Schubert status = DPP_STATUS_NEW_KEY_NEEDED; 2073*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 2074c1d255d3SCy Schubert else 2075c1d255d3SCy Schubert status = DPP_STATUS_CONFIGURE_FAILURE; 2076c1d255d3SCy Schubert forced_status: 20774bc52338SCy Schubert auth->conf_resp_status = status; 207885732ac8SCy Schubert 2079c1d255d3SCy Schubert /* { E-nonce, configurationObject[, sendConnStatus]}ke */ 208085732ac8SCy Schubert clear_len = 4 + e_nonce_len; 208185732ac8SCy Schubert if (conf) 208285732ac8SCy Schubert clear_len += 4 + wpabuf_len(conf); 2083c1d255d3SCy Schubert if (conf2) 2084c1d255d3SCy Schubert clear_len += 4 + wpabuf_len(conf2); 2085c1d255d3SCy Schubert if (env_data) 2086c1d255d3SCy Schubert clear_len += 4 + wpabuf_len(env_data); 2087c1d255d3SCy Schubert if (auth->peer_version >= 2 && auth->send_conn_status && 2088c1d255d3SCy Schubert netrole == DPP_NETROLE_STA) 2089c1d255d3SCy Schubert clear_len += 4; 2090c1d255d3SCy Schubert if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta && 2091c1d255d3SCy Schubert auth->conf_sta->csrattrs) 2092c1d255d3SCy Schubert clear_len += 4 + os_strlen(auth->conf_sta->csrattrs); 2093*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 2094*a90b9d01SCy Schubert if (status == DPP_STATUS_NEW_KEY_NEEDED) { 2095*a90b9d01SCy Schubert struct crypto_ec_key *new_pc; 2096*a90b9d01SCy Schubert 2097*a90b9d01SCy Schubert clear_len += 6; /* Finite Cyclic Group attribute */ 2098*a90b9d01SCy Schubert 2099*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 2100*a90b9d01SCy Schubert "DPP: Generate a new own protocol key for the curve %s", 2101*a90b9d01SCy Schubert auth->conf->net_access_key_curve->name); 2102*a90b9d01SCy Schubert new_pc = dpp_gen_keypair(auth->conf->net_access_key_curve); 2103*a90b9d01SCy Schubert if (!new_pc) { 2104*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to generate new Pc"); 2105*a90b9d01SCy Schubert return NULL; 2106*a90b9d01SCy Schubert } 2107*a90b9d01SCy Schubert pc = crypto_ec_key_get_pubkey_point(new_pc, 0); 2108*a90b9d01SCy Schubert if (!pc) { 2109*a90b9d01SCy Schubert crypto_ec_key_deinit(new_pc); 2110*a90b9d01SCy Schubert return NULL; 2111*a90b9d01SCy Schubert } 2112*a90b9d01SCy Schubert crypto_ec_key_deinit(auth->own_protocol_key); 2113*a90b9d01SCy Schubert auth->own_protocol_key = new_pc; 2114*a90b9d01SCy Schubert auth->new_curve = auth->conf->net_access_key_curve; 2115*a90b9d01SCy Schubert clear_len += 4 + wpabuf_len(pc); 2116*a90b9d01SCy Schubert } 2117*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 211885732ac8SCy Schubert clear = wpabuf_alloc(clear_len); 211985732ac8SCy Schubert attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; 212085732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 212185732ac8SCy Schubert if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) 212285732ac8SCy Schubert attr_len += 5; 212385732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 212485732ac8SCy Schubert msg = wpabuf_alloc(attr_len); 212585732ac8SCy Schubert if (!clear || !msg) 212685732ac8SCy Schubert goto fail; 212785732ac8SCy Schubert 212885732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 212985732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { 213085732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); 213185732ac8SCy Schubert goto skip_e_nonce; 213285732ac8SCy Schubert } 213385732ac8SCy Schubert if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { 213485732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); 213585732ac8SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 213685732ac8SCy Schubert wpabuf_put_le16(clear, e_nonce_len); 213785732ac8SCy Schubert wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); 213885732ac8SCy Schubert wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); 213985732ac8SCy Schubert goto skip_e_nonce; 214085732ac8SCy Schubert } 214185732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { 214285732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 214385732ac8SCy Schubert goto skip_wrapped_data; 214485732ac8SCy Schubert } 214585732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 214685732ac8SCy Schubert 214785732ac8SCy Schubert /* E-nonce */ 214885732ac8SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 214985732ac8SCy Schubert wpabuf_put_le16(clear, e_nonce_len); 215085732ac8SCy Schubert wpabuf_put_data(clear, e_nonce, e_nonce_len); 215185732ac8SCy Schubert 215285732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 215385732ac8SCy Schubert skip_e_nonce: 215485732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { 215585732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); 215685732ac8SCy Schubert goto skip_config_obj; 215785732ac8SCy Schubert } 215885732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 215985732ac8SCy Schubert 216085732ac8SCy Schubert if (conf) { 216185732ac8SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); 216285732ac8SCy Schubert wpabuf_put_le16(clear, wpabuf_len(conf)); 216385732ac8SCy Schubert wpabuf_put_buf(clear, conf); 216485732ac8SCy Schubert } 2165c1d255d3SCy Schubert if (auth->peer_version >= 2 && conf2) { 2166c1d255d3SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); 2167c1d255d3SCy Schubert wpabuf_put_le16(clear, wpabuf_len(conf2)); 2168c1d255d3SCy Schubert wpabuf_put_buf(clear, conf2); 2169c1d255d3SCy Schubert } else if (conf2) { 2170c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2171c1d255d3SCy Schubert "DPP: Second Config Object available, but peer does not support more than one"); 2172c1d255d3SCy Schubert } 2173c1d255d3SCy Schubert if (env_data) { 2174c1d255d3SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENVELOPED_DATA); 2175c1d255d3SCy Schubert wpabuf_put_le16(clear, wpabuf_len(env_data)); 2176c1d255d3SCy Schubert wpabuf_put_buf(clear, env_data); 2177c1d255d3SCy Schubert } 2178c1d255d3SCy Schubert 2179c1d255d3SCy Schubert if (auth->peer_version >= 2 && auth->send_conn_status && 2180c1d255d3SCy Schubert netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) { 2181c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: sendConnStatus"); 2182c1d255d3SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS); 2183c1d255d3SCy Schubert wpabuf_put_le16(clear, 0); 2184c1d255d3SCy Schubert } 2185c1d255d3SCy Schubert 2186c1d255d3SCy Schubert if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta && 2187c1d255d3SCy Schubert auth->conf_sta->csrattrs) { 2188c1d255d3SCy Schubert auth->waiting_csr = true; 2189c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request"); 2190c1d255d3SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ); 2191c1d255d3SCy Schubert wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs)); 2192c1d255d3SCy Schubert wpabuf_put_str(clear, auth->conf_sta->csrattrs); 2193c1d255d3SCy Schubert } 219485732ac8SCy Schubert 2195*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 2196*a90b9d01SCy Schubert if (status == DPP_STATUS_NEW_KEY_NEEDED && auth->conf && 2197*a90b9d01SCy Schubert auth->conf->net_access_key_curve) { 2198*a90b9d01SCy Schubert u16 ike_group = auth->conf->net_access_key_curve->ike_group; 2199*a90b9d01SCy Schubert 2200*a90b9d01SCy Schubert /* Finite Cyclic Group attribute */ 2201*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u", 2202*a90b9d01SCy Schubert ike_group); 2203*a90b9d01SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_FINITE_CYCLIC_GROUP); 2204*a90b9d01SCy Schubert wpabuf_put_le16(clear, 2); 2205*a90b9d01SCy Schubert wpabuf_put_le16(clear, ike_group); 2206*a90b9d01SCy Schubert 2207*a90b9d01SCy Schubert if (pc) { 2208*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Pc"); 2209*a90b9d01SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_R_PROTOCOL_KEY); 2210*a90b9d01SCy Schubert wpabuf_put_le16(clear, wpabuf_len(pc)); 2211*a90b9d01SCy Schubert wpabuf_put_buf(clear, pc); 2212*a90b9d01SCy Schubert } 2213*a90b9d01SCy Schubert } 2214*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 2215*a90b9d01SCy Schubert 221685732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 221785732ac8SCy Schubert skip_config_obj: 221885732ac8SCy Schubert if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { 221985732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - Status"); 222085732ac8SCy Schubert goto skip_status; 222185732ac8SCy Schubert } 222285732ac8SCy Schubert if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { 222385732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); 222485732ac8SCy Schubert status = 255; 222585732ac8SCy Schubert } 222685732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 222785732ac8SCy Schubert 222885732ac8SCy Schubert /* DPP Status */ 222985732ac8SCy Schubert dpp_build_attr_status(msg, status); 223085732ac8SCy Schubert 223185732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 223285732ac8SCy Schubert skip_status: 223385732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 223485732ac8SCy Schubert 223585732ac8SCy Schubert addr[0] = wpabuf_head(msg); 223685732ac8SCy Schubert len[0] = wpabuf_len(msg); 223785732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); 223885732ac8SCy Schubert 223985732ac8SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 224085732ac8SCy Schubert wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 224185732ac8SCy Schubert wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 224285732ac8SCy Schubert 224385732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 224485732ac8SCy Schubert if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 224585732ac8SCy Schubert wpabuf_head(clear), wpabuf_len(clear), 224685732ac8SCy Schubert 1, addr, len, wrapped) < 0) 224785732ac8SCy Schubert goto fail; 224885732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 224985732ac8SCy Schubert wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); 225085732ac8SCy Schubert 225185732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 225285732ac8SCy Schubert if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { 225385732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 225485732ac8SCy Schubert dpp_build_attr_status(msg, DPP_STATUS_OK); 225585732ac8SCy Schubert } 225685732ac8SCy Schubert skip_wrapped_data: 225785732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 225885732ac8SCy Schubert 225985732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 226085732ac8SCy Schubert "DPP: Configuration Response attributes", msg); 226185732ac8SCy Schubert out: 2262c1d255d3SCy Schubert wpabuf_clear_free(conf); 2263c1d255d3SCy Schubert wpabuf_clear_free(conf2); 2264c1d255d3SCy Schubert wpabuf_clear_free(env_data); 2265c1d255d3SCy Schubert wpabuf_clear_free(clear); 2266*a90b9d01SCy Schubert wpabuf_free(pc); 226785732ac8SCy Schubert 226885732ac8SCy Schubert return msg; 226985732ac8SCy Schubert fail: 227085732ac8SCy Schubert wpabuf_free(msg); 227185732ac8SCy Schubert msg = NULL; 227285732ac8SCy Schubert goto out; 227385732ac8SCy Schubert } 227485732ac8SCy Schubert 227585732ac8SCy Schubert 227685732ac8SCy Schubert struct wpabuf * 227785732ac8SCy Schubert dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, 227885732ac8SCy Schubert size_t attr_len) 227985732ac8SCy Schubert { 228085732ac8SCy Schubert const u8 *wrapped_data, *e_nonce, *config_attr; 228185732ac8SCy Schubert u16 wrapped_data_len, e_nonce_len, config_attr_len; 228285732ac8SCy Schubert u8 *unwrapped = NULL; 228385732ac8SCy Schubert size_t unwrapped_len = 0; 228485732ac8SCy Schubert struct wpabuf *resp = NULL; 228585732ac8SCy Schubert struct json_token *root = NULL, *token; 2286c1d255d3SCy Schubert enum dpp_netrole netrole; 2287c1d255d3SCy Schubert struct wpabuf *cert_req = NULL; 2288*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 2289*a90b9d01SCy Schubert const u8 *i_proto; 2290*a90b9d01SCy Schubert u16 i_proto_len; 2291*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 229285732ac8SCy Schubert 229385732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 229485732ac8SCy Schubert if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { 229585732ac8SCy Schubert wpa_printf(MSG_INFO, 229685732ac8SCy Schubert "DPP: TESTING - stop at Config Request"); 229785732ac8SCy Schubert return NULL; 229885732ac8SCy Schubert } 229985732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 230085732ac8SCy Schubert 230185732ac8SCy Schubert if (dpp_check_attrs(attr_start, attr_len) < 0) { 230285732ac8SCy Schubert dpp_auth_fail(auth, "Invalid attribute in config request"); 230385732ac8SCy Schubert return NULL; 230485732ac8SCy Schubert } 230585732ac8SCy Schubert 230685732ac8SCy Schubert wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 230785732ac8SCy Schubert &wrapped_data_len); 230885732ac8SCy Schubert if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 230985732ac8SCy Schubert dpp_auth_fail(auth, 231085732ac8SCy Schubert "Missing or invalid required Wrapped Data attribute"); 231185732ac8SCy Schubert return NULL; 231285732ac8SCy Schubert } 231385732ac8SCy Schubert 231485732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 231585732ac8SCy Schubert wrapped_data, wrapped_data_len); 231685732ac8SCy Schubert unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 231785732ac8SCy Schubert unwrapped = os_malloc(unwrapped_len); 231885732ac8SCy Schubert if (!unwrapped) 231985732ac8SCy Schubert return NULL; 232085732ac8SCy Schubert if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 232185732ac8SCy Schubert wrapped_data, wrapped_data_len, 232285732ac8SCy Schubert 0, NULL, NULL, unwrapped) < 0) { 232385732ac8SCy Schubert dpp_auth_fail(auth, "AES-SIV decryption failed"); 232485732ac8SCy Schubert goto fail; 232585732ac8SCy Schubert } 232685732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 232785732ac8SCy Schubert unwrapped, unwrapped_len); 232885732ac8SCy Schubert 232985732ac8SCy Schubert if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 233085732ac8SCy Schubert dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 233185732ac8SCy Schubert goto fail; 233285732ac8SCy Schubert } 233385732ac8SCy Schubert 233485732ac8SCy Schubert e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 233585732ac8SCy Schubert DPP_ATTR_ENROLLEE_NONCE, 233685732ac8SCy Schubert &e_nonce_len); 233785732ac8SCy Schubert if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 233885732ac8SCy Schubert dpp_auth_fail(auth, 233985732ac8SCy Schubert "Missing or invalid Enrollee Nonce attribute"); 234085732ac8SCy Schubert goto fail; 234185732ac8SCy Schubert } 234285732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 23434bc52338SCy Schubert os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); 234485732ac8SCy Schubert 2345*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 2346*a90b9d01SCy Schubert i_proto = dpp_get_attr(unwrapped, unwrapped_len, 2347*a90b9d01SCy Schubert DPP_ATTR_I_PROTOCOL_KEY, &i_proto_len); 2348*a90b9d01SCy Schubert if (i_proto && !auth->waiting_new_key) { 2349*a90b9d01SCy Schubert dpp_auth_fail(auth, 2350*a90b9d01SCy Schubert "Enrollee included a new protocol key even though one was not expected"); 2351*a90b9d01SCy Schubert goto fail; 2352*a90b9d01SCy Schubert } 2353*a90b9d01SCy Schubert if (i_proto) { 2354*a90b9d01SCy Schubert struct crypto_ec_key *pe; 2355*a90b9d01SCy Schubert u8 auth_i[DPP_MAX_HASH_LEN]; 2356*a90b9d01SCy Schubert const u8 *rx_auth_i; 2357*a90b9d01SCy Schubert u16 rx_auth_i_len; 2358*a90b9d01SCy Schubert 2359*a90b9d01SCy Schubert wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key (new Pe)", 2360*a90b9d01SCy Schubert i_proto, i_proto_len); 2361*a90b9d01SCy Schubert 2362*a90b9d01SCy Schubert pe = dpp_set_pubkey_point(auth->own_protocol_key, 2363*a90b9d01SCy Schubert i_proto, i_proto_len); 2364*a90b9d01SCy Schubert if (!pe) { 2365*a90b9d01SCy Schubert dpp_auth_fail(auth, 2366*a90b9d01SCy Schubert "Invalid Initiator Protocol Key (Pe)"); 2367*a90b9d01SCy Schubert goto fail; 2368*a90b9d01SCy Schubert } 2369*a90b9d01SCy Schubert dpp_debug_print_key("New Peer Protocol Key (Pe)", pe); 2370*a90b9d01SCy Schubert crypto_ec_key_deinit(auth->peer_protocol_key); 2371*a90b9d01SCy Schubert auth->peer_protocol_key = pe; 2372*a90b9d01SCy Schubert auth->new_key_received = true; 2373*a90b9d01SCy Schubert auth->waiting_new_key = false; 2374*a90b9d01SCy Schubert 2375*a90b9d01SCy Schubert if (dpp_derive_auth_i(auth, auth_i) < 0) 2376*a90b9d01SCy Schubert goto fail; 2377*a90b9d01SCy Schubert 2378*a90b9d01SCy Schubert rx_auth_i = dpp_get_attr(unwrapped, unwrapped_len, 2379*a90b9d01SCy Schubert DPP_ATTR_I_AUTH_TAG, &rx_auth_i_len); 2380*a90b9d01SCy Schubert if (!rx_auth_i) { 2381*a90b9d01SCy Schubert dpp_auth_fail(auth, 2382*a90b9d01SCy Schubert "Missing Initiator Authentication Tag"); 2383*a90b9d01SCy Schubert goto fail; 2384*a90b9d01SCy Schubert } 2385*a90b9d01SCy Schubert if (rx_auth_i_len != auth->curve->hash_len || 2386*a90b9d01SCy Schubert os_memcmp(rx_auth_i, auth_i, auth->curve->hash_len) != 0) { 2387*a90b9d01SCy Schubert dpp_auth_fail(auth, 2388*a90b9d01SCy Schubert "Mismatch in Initiator Authenticating Tag"); 2389*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Received Auth-I", 2390*a90b9d01SCy Schubert rx_auth_i, rx_auth_i_len); 2391*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Derived Auth-I'", 2392*a90b9d01SCy Schubert auth_i, auth->curve->hash_len); 2393*a90b9d01SCy Schubert goto fail; 2394*a90b9d01SCy Schubert } 2395*a90b9d01SCy Schubert } 2396*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 2397*a90b9d01SCy Schubert 239885732ac8SCy Schubert config_attr = dpp_get_attr(unwrapped, unwrapped_len, 239985732ac8SCy Schubert DPP_ATTR_CONFIG_ATTR_OBJ, 240085732ac8SCy Schubert &config_attr_len); 240185732ac8SCy Schubert if (!config_attr) { 240285732ac8SCy Schubert dpp_auth_fail(auth, 240385732ac8SCy Schubert "Missing or invalid Config Attributes attribute"); 240485732ac8SCy Schubert goto fail; 240585732ac8SCy Schubert } 240685732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", 240785732ac8SCy Schubert config_attr, config_attr_len); 240885732ac8SCy Schubert 240985732ac8SCy Schubert root = json_parse((const char *) config_attr, config_attr_len); 241085732ac8SCy Schubert if (!root) { 241185732ac8SCy Schubert dpp_auth_fail(auth, "Could not parse Config Attributes"); 241285732ac8SCy Schubert goto fail; 241385732ac8SCy Schubert } 241485732ac8SCy Schubert 241585732ac8SCy Schubert token = json_get_member(root, "name"); 241685732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 241785732ac8SCy Schubert dpp_auth_fail(auth, "No Config Attributes - name"); 241885732ac8SCy Schubert goto fail; 241985732ac8SCy Schubert } 242085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); 2421*a90b9d01SCy Schubert os_free(auth->e_name); 2422*a90b9d01SCy Schubert auth->e_name = os_strdup(token->string); 242385732ac8SCy Schubert 242485732ac8SCy Schubert token = json_get_member(root, "wi-fi_tech"); 242585732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 242685732ac8SCy Schubert dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); 242785732ac8SCy Schubert goto fail; 242885732ac8SCy Schubert } 242985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); 243085732ac8SCy Schubert if (os_strcmp(token->string, "infra") != 0) { 243185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", 243285732ac8SCy Schubert token->string); 243385732ac8SCy Schubert dpp_auth_fail(auth, "Unsupported wi-fi_tech"); 243485732ac8SCy Schubert goto fail; 243585732ac8SCy Schubert } 243685732ac8SCy Schubert 243785732ac8SCy Schubert token = json_get_member(root, "netRole"); 243885732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 243985732ac8SCy Schubert dpp_auth_fail(auth, "No Config Attributes - netRole"); 244085732ac8SCy Schubert goto fail; 244185732ac8SCy Schubert } 244285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); 244385732ac8SCy Schubert if (os_strcmp(token->string, "sta") == 0) { 2444c1d255d3SCy Schubert netrole = DPP_NETROLE_STA; 244585732ac8SCy Schubert } else if (os_strcmp(token->string, "ap") == 0) { 2446c1d255d3SCy Schubert netrole = DPP_NETROLE_AP; 2447c1d255d3SCy Schubert } else if (os_strcmp(token->string, "configurator") == 0) { 2448c1d255d3SCy Schubert netrole = DPP_NETROLE_CONFIGURATOR; 244985732ac8SCy Schubert } else { 245085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", 245185732ac8SCy Schubert token->string); 245285732ac8SCy Schubert dpp_auth_fail(auth, "Unsupported netRole"); 245385732ac8SCy Schubert goto fail; 245485732ac8SCy Schubert } 2455c1d255d3SCy Schubert auth->e_netrole = netrole; 245685732ac8SCy Schubert 2457c1d255d3SCy Schubert token = json_get_member(root, "mudurl"); 2458c1d255d3SCy Schubert if (token && token->type == JSON_STRING) { 2459c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string); 2460c1d255d3SCy Schubert wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_MUD_URL "%s", 2461c1d255d3SCy Schubert token->string); 2462*a90b9d01SCy Schubert os_free(auth->e_mud_url); 2463*a90b9d01SCy Schubert auth->e_mud_url = os_strdup(token->string); 2464c1d255d3SCy Schubert } 2465c1d255d3SCy Schubert 2466c1d255d3SCy Schubert token = json_get_member(root, "bandSupport"); 2467c1d255d3SCy Schubert if (token && token->type == JSON_ARRAY) { 2468c1d255d3SCy Schubert int *opclass = NULL; 2469c1d255d3SCy Schubert char txt[200], *pos, *end; 2470c1d255d3SCy Schubert int i, res; 2471c1d255d3SCy Schubert 2472c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: bandSupport"); 2473c1d255d3SCy Schubert token = token->child; 2474c1d255d3SCy Schubert while (token) { 2475c1d255d3SCy Schubert if (token->type != JSON_NUMBER) { 2476c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2477c1d255d3SCy Schubert "DPP: Invalid bandSupport array member type"); 2478c1d255d3SCy Schubert } else { 2479c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2480c1d255d3SCy Schubert "DPP: Supported global operating class: %d", 2481c1d255d3SCy Schubert token->number); 2482c1d255d3SCy Schubert int_array_add_unique(&opclass, token->number); 2483c1d255d3SCy Schubert } 2484c1d255d3SCy Schubert token = token->sibling; 2485c1d255d3SCy Schubert } 2486c1d255d3SCy Schubert 2487c1d255d3SCy Schubert txt[0] = '\0'; 2488c1d255d3SCy Schubert pos = txt; 2489c1d255d3SCy Schubert end = txt + sizeof(txt); 2490c1d255d3SCy Schubert for (i = 0; opclass && opclass[i]; i++) { 2491c1d255d3SCy Schubert res = os_snprintf(pos, end - pos, "%s%d", 2492c1d255d3SCy Schubert pos == txt ? "" : ",", opclass[i]); 2493c1d255d3SCy Schubert if (os_snprintf_error(end - pos, res)) { 2494c1d255d3SCy Schubert *pos = '\0'; 2495c1d255d3SCy Schubert break; 2496c1d255d3SCy Schubert } 2497c1d255d3SCy Schubert pos += res; 2498c1d255d3SCy Schubert } 2499*a90b9d01SCy Schubert os_free(auth->e_band_support); 2500*a90b9d01SCy Schubert auth->e_band_support = opclass; 2501c1d255d3SCy Schubert wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_BAND_SUPPORT "%s", 2502c1d255d3SCy Schubert txt); 2503c1d255d3SCy Schubert } 2504c1d255d3SCy Schubert 2505c1d255d3SCy Schubert #ifdef CONFIG_DPP2 2506c1d255d3SCy Schubert cert_req = json_get_member_base64(root, "pkcs10"); 2507c1d255d3SCy Schubert if (cert_req) { 2508c1d255d3SCy Schubert char *txt; 2509c1d255d3SCy Schubert int id; 2510c1d255d3SCy Schubert 2511c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req); 2512c1d255d3SCy Schubert if (dpp_validate_csr(auth, cert_req) < 0) { 2513c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: CSR is not valid"); 2514c1d255d3SCy Schubert auth->force_conf_resp_status = DPP_STATUS_CSR_BAD; 2515c1d255d3SCy Schubert goto cont; 2516c1d255d3SCy Schubert } 2517c1d255d3SCy Schubert 2518*a90b9d01SCy Schubert id = dpp_get_peer_bi_id(auth); 2519*a90b9d01SCy Schubert if (id < 0) 2520c1d255d3SCy Schubert goto fail; 2521c1d255d3SCy Schubert 2522c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA"); 2523c1d255d3SCy Schubert txt = base64_encode_no_lf(wpabuf_head(cert_req), 2524c1d255d3SCy Schubert wpabuf_len(cert_req), NULL); 2525c1d255d3SCy Schubert if (!txt) 2526c1d255d3SCy Schubert goto fail; 2527c1d255d3SCy Schubert 2528c1d255d3SCy Schubert wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s", 2529c1d255d3SCy Schubert id, txt); 2530c1d255d3SCy Schubert os_free(txt); 2531c1d255d3SCy Schubert auth->waiting_csr = false; 2532c1d255d3SCy Schubert auth->waiting_cert = true; 2533c1d255d3SCy Schubert goto fail; 2534c1d255d3SCy Schubert } 2535c1d255d3SCy Schubert cont: 2536c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 2537c1d255d3SCy Schubert 2538c1d255d3SCy Schubert resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole, 2539c1d255d3SCy Schubert cert_req); 254085732ac8SCy Schubert 254185732ac8SCy Schubert fail: 2542c1d255d3SCy Schubert wpabuf_free(cert_req); 254385732ac8SCy Schubert json_free(root); 254485732ac8SCy Schubert os_free(unwrapped); 254585732ac8SCy Schubert return resp; 254685732ac8SCy Schubert } 254785732ac8SCy Schubert 254885732ac8SCy Schubert 2549c1d255d3SCy Schubert static int dpp_parse_cred_legacy(struct dpp_config_obj *conf, 255085732ac8SCy Schubert struct json_token *cred) 255185732ac8SCy Schubert { 255285732ac8SCy Schubert struct json_token *pass, *psk_hex; 255385732ac8SCy Schubert 255485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); 255585732ac8SCy Schubert 255685732ac8SCy Schubert pass = json_get_member(cred, "pass"); 255785732ac8SCy Schubert psk_hex = json_get_member(cred, "psk_hex"); 255885732ac8SCy Schubert 255985732ac8SCy Schubert if (pass && pass->type == JSON_STRING) { 256085732ac8SCy Schubert size_t len = os_strlen(pass->string); 256185732ac8SCy Schubert 256285732ac8SCy Schubert wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", 256385732ac8SCy Schubert pass->string, len); 256485732ac8SCy Schubert if (len < 8 || len > 63) 256585732ac8SCy Schubert return -1; 2566c1d255d3SCy Schubert os_strlcpy(conf->passphrase, pass->string, 2567c1d255d3SCy Schubert sizeof(conf->passphrase)); 256885732ac8SCy Schubert } else if (psk_hex && psk_hex->type == JSON_STRING) { 2569c1d255d3SCy Schubert if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) { 257085732ac8SCy Schubert wpa_printf(MSG_DEBUG, 257185732ac8SCy Schubert "DPP: Unexpected psk_hex with akm=sae"); 257285732ac8SCy Schubert return -1; 257385732ac8SCy Schubert } 257485732ac8SCy Schubert if (os_strlen(psk_hex->string) != PMK_LEN * 2 || 2575c1d255d3SCy Schubert hexstr2bin(psk_hex->string, conf->psk, PMK_LEN) < 0) { 257685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); 257785732ac8SCy Schubert return -1; 257885732ac8SCy Schubert } 257985732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", 2580c1d255d3SCy Schubert conf->psk, PMK_LEN); 2581c1d255d3SCy Schubert conf->psk_set = 1; 258285732ac8SCy Schubert } else { 258385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); 258485732ac8SCy Schubert return -1; 258585732ac8SCy Schubert } 258685732ac8SCy Schubert 2587c1d255d3SCy Schubert if (dpp_akm_sae(conf->akm) && !conf->passphrase[0]) { 258885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); 258985732ac8SCy Schubert return -1; 259085732ac8SCy Schubert } 259185732ac8SCy Schubert 259285732ac8SCy Schubert return 0; 259385732ac8SCy Schubert } 259485732ac8SCy Schubert 259585732ac8SCy Schubert 25964b72b91aSCy Schubert struct crypto_ec_key * dpp_parse_jwk(struct json_token *jwk, 259785732ac8SCy Schubert const struct dpp_curve_params **key_curve) 259885732ac8SCy Schubert { 259985732ac8SCy Schubert struct json_token *token; 260085732ac8SCy Schubert const struct dpp_curve_params *curve; 260185732ac8SCy Schubert struct wpabuf *x = NULL, *y = NULL; 26024b72b91aSCy Schubert struct crypto_ec_key *key = NULL; 260385732ac8SCy Schubert 260485732ac8SCy Schubert token = json_get_member(jwk, "kty"); 260585732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 260685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); 260785732ac8SCy Schubert goto fail; 260885732ac8SCy Schubert } 260985732ac8SCy Schubert if (os_strcmp(token->string, "EC") != 0) { 261085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", 261185732ac8SCy Schubert token->string); 261285732ac8SCy Schubert goto fail; 261385732ac8SCy Schubert } 261485732ac8SCy Schubert 261585732ac8SCy Schubert token = json_get_member(jwk, "crv"); 261685732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 261785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); 261885732ac8SCy Schubert goto fail; 261985732ac8SCy Schubert } 262085732ac8SCy Schubert curve = dpp_get_curve_jwk_crv(token->string); 262185732ac8SCy Schubert if (!curve) { 262285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", 262385732ac8SCy Schubert token->string); 262485732ac8SCy Schubert goto fail; 262585732ac8SCy Schubert } 262685732ac8SCy Schubert 262785732ac8SCy Schubert x = json_get_member_base64url(jwk, "x"); 262885732ac8SCy Schubert if (!x) { 262985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); 263085732ac8SCy Schubert goto fail; 263185732ac8SCy Schubert } 263285732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); 263385732ac8SCy Schubert if (wpabuf_len(x) != curve->prime_len) { 263485732ac8SCy Schubert wpa_printf(MSG_DEBUG, 263585732ac8SCy Schubert "DPP: Unexpected JWK x length %u (expected %u for curve %s)", 263685732ac8SCy Schubert (unsigned int) wpabuf_len(x), 263785732ac8SCy Schubert (unsigned int) curve->prime_len, curve->name); 263885732ac8SCy Schubert goto fail; 263985732ac8SCy Schubert } 264085732ac8SCy Schubert 264185732ac8SCy Schubert y = json_get_member_base64url(jwk, "y"); 264285732ac8SCy Schubert if (!y) { 264385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); 264485732ac8SCy Schubert goto fail; 264585732ac8SCy Schubert } 264685732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); 264785732ac8SCy Schubert if (wpabuf_len(y) != curve->prime_len) { 264885732ac8SCy Schubert wpa_printf(MSG_DEBUG, 264985732ac8SCy Schubert "DPP: Unexpected JWK y length %u (expected %u for curve %s)", 265085732ac8SCy Schubert (unsigned int) wpabuf_len(y), 265185732ac8SCy Schubert (unsigned int) curve->prime_len, curve->name); 265285732ac8SCy Schubert goto fail; 265385732ac8SCy Schubert } 265485732ac8SCy Schubert 26554b72b91aSCy Schubert key = crypto_ec_key_set_pub(curve->ike_group, wpabuf_head(x), 26564b72b91aSCy Schubert wpabuf_head(y), wpabuf_len(x)); 26574b72b91aSCy Schubert if (!key) 265885732ac8SCy Schubert goto fail; 265985732ac8SCy Schubert 266085732ac8SCy Schubert *key_curve = curve; 266185732ac8SCy Schubert 266285732ac8SCy Schubert fail: 266385732ac8SCy Schubert wpabuf_free(x); 266485732ac8SCy Schubert wpabuf_free(y); 266585732ac8SCy Schubert 26664b72b91aSCy Schubert return key; 266785732ac8SCy Schubert } 266885732ac8SCy Schubert 266985732ac8SCy Schubert 267085732ac8SCy Schubert int dpp_key_expired(const char *timestamp, os_time_t *expiry) 267185732ac8SCy Schubert { 267285732ac8SCy Schubert struct os_time now; 267385732ac8SCy Schubert unsigned int year, month, day, hour, min, sec; 267485732ac8SCy Schubert os_time_t utime; 267585732ac8SCy Schubert const char *pos; 267685732ac8SCy Schubert 267785732ac8SCy Schubert /* ISO 8601 date and time: 267885732ac8SCy Schubert * <date>T<time> 267985732ac8SCy Schubert * YYYY-MM-DDTHH:MM:SSZ 268085732ac8SCy Schubert * YYYY-MM-DDTHH:MM:SS+03:00 268185732ac8SCy Schubert */ 268285732ac8SCy Schubert if (os_strlen(timestamp) < 19) { 268385732ac8SCy Schubert wpa_printf(MSG_DEBUG, 268485732ac8SCy Schubert "DPP: Too short timestamp - assume expired key"); 268585732ac8SCy Schubert return 1; 268685732ac8SCy Schubert } 268785732ac8SCy Schubert if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", 268885732ac8SCy Schubert &year, &month, &day, &hour, &min, &sec) != 6) { 268985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 269085732ac8SCy Schubert "DPP: Failed to parse expiration day - assume expired key"); 269185732ac8SCy Schubert return 1; 269285732ac8SCy Schubert } 269385732ac8SCy Schubert 269485732ac8SCy Schubert if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) { 269585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 269685732ac8SCy Schubert "DPP: Invalid date/time information - assume expired key"); 269785732ac8SCy Schubert return 1; 269885732ac8SCy Schubert } 269985732ac8SCy Schubert 270085732ac8SCy Schubert pos = timestamp + 19; 270185732ac8SCy Schubert if (*pos == 'Z' || *pos == '\0') { 270285732ac8SCy Schubert /* In UTC - no need to adjust */ 270385732ac8SCy Schubert } else if (*pos == '-' || *pos == '+') { 270485732ac8SCy Schubert int items; 270585732ac8SCy Schubert 270685732ac8SCy Schubert /* Adjust local time to UTC */ 270785732ac8SCy Schubert items = sscanf(pos + 1, "%02u:%02u", &hour, &min); 270885732ac8SCy Schubert if (items < 1) { 270985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 271085732ac8SCy Schubert "DPP: Invalid time zone designator (%s) - assume expired key", 271185732ac8SCy Schubert pos); 271285732ac8SCy Schubert return 1; 271385732ac8SCy Schubert } 271485732ac8SCy Schubert if (*pos == '-') 271585732ac8SCy Schubert utime += 3600 * hour; 271685732ac8SCy Schubert if (*pos == '+') 271785732ac8SCy Schubert utime -= 3600 * hour; 271885732ac8SCy Schubert if (items > 1) { 271985732ac8SCy Schubert if (*pos == '-') 272085732ac8SCy Schubert utime += 60 * min; 272185732ac8SCy Schubert if (*pos == '+') 272285732ac8SCy Schubert utime -= 60 * min; 272385732ac8SCy Schubert } 272485732ac8SCy Schubert } else { 272585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 272685732ac8SCy Schubert "DPP: Invalid time zone designator (%s) - assume expired key", 272785732ac8SCy Schubert pos); 272885732ac8SCy Schubert return 1; 272985732ac8SCy Schubert } 273085732ac8SCy Schubert if (expiry) 273185732ac8SCy Schubert *expiry = utime; 273285732ac8SCy Schubert 273385732ac8SCy Schubert if (os_get_time(&now) < 0) { 273485732ac8SCy Schubert wpa_printf(MSG_DEBUG, 273585732ac8SCy Schubert "DPP: Cannot get current time - assume expired key"); 273685732ac8SCy Schubert return 1; 273785732ac8SCy Schubert } 273885732ac8SCy Schubert 273985732ac8SCy Schubert if (now.sec > utime) { 274085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)", 274185732ac8SCy Schubert utime, now.sec); 274285732ac8SCy Schubert return 1; 274385732ac8SCy Schubert } 274485732ac8SCy Schubert 274585732ac8SCy Schubert return 0; 274685732ac8SCy Schubert } 274785732ac8SCy Schubert 274885732ac8SCy Schubert 274985732ac8SCy Schubert static int dpp_parse_connector(struct dpp_authentication *auth, 2750c1d255d3SCy Schubert struct dpp_config_obj *conf, 275185732ac8SCy Schubert const unsigned char *payload, 275285732ac8SCy Schubert u16 payload_len) 275385732ac8SCy Schubert { 275485732ac8SCy Schubert struct json_token *root, *groups, *netkey, *token; 275585732ac8SCy Schubert int ret = -1; 27564b72b91aSCy Schubert struct crypto_ec_key *key = NULL; 275785732ac8SCy Schubert const struct dpp_curve_params *curve; 275885732ac8SCy Schubert unsigned int rules = 0; 275985732ac8SCy Schubert 276085732ac8SCy Schubert root = json_parse((const char *) payload, payload_len); 276185732ac8SCy Schubert if (!root) { 276285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); 276385732ac8SCy Schubert goto fail; 276485732ac8SCy Schubert } 276585732ac8SCy Schubert 276685732ac8SCy Schubert groups = json_get_member(root, "groups"); 276785732ac8SCy Schubert if (!groups || groups->type != JSON_ARRAY) { 276885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No groups array found"); 276985732ac8SCy Schubert goto skip_groups; 277085732ac8SCy Schubert } 277185732ac8SCy Schubert for (token = groups->child; token; token = token->sibling) { 277285732ac8SCy Schubert struct json_token *id, *role; 277385732ac8SCy Schubert 277485732ac8SCy Schubert id = json_get_member(token, "groupId"); 277585732ac8SCy Schubert if (!id || id->type != JSON_STRING) { 277685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Missing groupId string"); 277785732ac8SCy Schubert goto fail; 277885732ac8SCy Schubert } 277985732ac8SCy Schubert 278085732ac8SCy Schubert role = json_get_member(token, "netRole"); 278185732ac8SCy Schubert if (!role || role->type != JSON_STRING) { 278285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Missing netRole string"); 278385732ac8SCy Schubert goto fail; 278485732ac8SCy Schubert } 278585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 278685732ac8SCy Schubert "DPP: connector group: groupId='%s' netRole='%s'", 278785732ac8SCy Schubert id->string, role->string); 278885732ac8SCy Schubert rules++; 278985732ac8SCy Schubert } 279085732ac8SCy Schubert skip_groups: 279185732ac8SCy Schubert 279285732ac8SCy Schubert if (!rules) { 279385732ac8SCy Schubert wpa_printf(MSG_DEBUG, 279485732ac8SCy Schubert "DPP: Connector includes no groups"); 279585732ac8SCy Schubert goto fail; 279685732ac8SCy Schubert } 279785732ac8SCy Schubert 279885732ac8SCy Schubert token = json_get_member(root, "expiry"); 279985732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 280085732ac8SCy Schubert wpa_printf(MSG_DEBUG, 280185732ac8SCy Schubert "DPP: No expiry string found - connector does not expire"); 280285732ac8SCy Schubert } else { 280385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); 280485732ac8SCy Schubert if (dpp_key_expired(token->string, 280585732ac8SCy Schubert &auth->net_access_key_expiry)) { 280685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 280785732ac8SCy Schubert "DPP: Connector (netAccessKey) has expired"); 280885732ac8SCy Schubert goto fail; 280985732ac8SCy Schubert } 281085732ac8SCy Schubert } 281185732ac8SCy Schubert 281285732ac8SCy Schubert netkey = json_get_member(root, "netAccessKey"); 281385732ac8SCy Schubert if (!netkey || netkey->type != JSON_OBJECT) { 281485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); 281585732ac8SCy Schubert goto fail; 281685732ac8SCy Schubert } 281785732ac8SCy Schubert 281885732ac8SCy Schubert key = dpp_parse_jwk(netkey, &curve); 281985732ac8SCy Schubert if (!key) 282085732ac8SCy Schubert goto fail; 282185732ac8SCy Schubert dpp_debug_print_key("DPP: Received netAccessKey", key); 282285732ac8SCy Schubert 28234b72b91aSCy Schubert if (crypto_ec_key_cmp(key, auth->own_protocol_key)) { 282485732ac8SCy Schubert wpa_printf(MSG_DEBUG, 282585732ac8SCy Schubert "DPP: netAccessKey in connector does not match own protocol key"); 282685732ac8SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 282785732ac8SCy Schubert if (auth->ignore_netaccesskey_mismatch) { 282885732ac8SCy Schubert wpa_printf(MSG_DEBUG, 282985732ac8SCy Schubert "DPP: TESTING - skip netAccessKey mismatch"); 283085732ac8SCy Schubert } else { 283185732ac8SCy Schubert goto fail; 283285732ac8SCy Schubert } 283385732ac8SCy Schubert #else /* CONFIG_TESTING_OPTIONS */ 283485732ac8SCy Schubert goto fail; 283585732ac8SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 283685732ac8SCy Schubert } 283785732ac8SCy Schubert 283885732ac8SCy Schubert ret = 0; 283985732ac8SCy Schubert fail: 28404b72b91aSCy Schubert crypto_ec_key_deinit(key); 284185732ac8SCy Schubert json_free(root); 284285732ac8SCy Schubert return ret; 284385732ac8SCy Schubert } 284485732ac8SCy Schubert 284585732ac8SCy Schubert 28464b72b91aSCy Schubert static void dpp_copy_csign(struct dpp_config_obj *conf, 28474b72b91aSCy Schubert struct crypto_ec_key *csign) 284885732ac8SCy Schubert { 28494b72b91aSCy Schubert struct wpabuf *c_sign_key; 285085732ac8SCy Schubert 28514b72b91aSCy Schubert c_sign_key = crypto_ec_key_get_subject_public_key(csign); 28524b72b91aSCy Schubert if (!c_sign_key) 285385732ac8SCy Schubert return; 28544b72b91aSCy Schubert 2855c1d255d3SCy Schubert wpabuf_free(conf->c_sign_key); 28564b72b91aSCy Schubert conf->c_sign_key = c_sign_key; 285785732ac8SCy Schubert } 285885732ac8SCy Schubert 285985732ac8SCy Schubert 28604b72b91aSCy Schubert static void dpp_copy_ppkey(struct dpp_config_obj *conf, 28614b72b91aSCy Schubert struct crypto_ec_key *ppkey) 2862c1d255d3SCy Schubert { 28634b72b91aSCy Schubert struct wpabuf *pp_key; 2864c1d255d3SCy Schubert 28654b72b91aSCy Schubert pp_key = crypto_ec_key_get_subject_public_key(ppkey); 28664b72b91aSCy Schubert if (!pp_key) 2867c1d255d3SCy Schubert return; 28684b72b91aSCy Schubert 2869c1d255d3SCy Schubert wpabuf_free(conf->pp_key); 28704b72b91aSCy Schubert conf->pp_key = pp_key; 2871c1d255d3SCy Schubert } 2872c1d255d3SCy Schubert 2873c1d255d3SCy Schubert 2874c1d255d3SCy Schubert static void dpp_copy_netaccesskey(struct dpp_authentication *auth, 2875c1d255d3SCy Schubert struct dpp_config_obj *conf) 287685732ac8SCy Schubert { 28774b72b91aSCy Schubert struct wpabuf *net_access_key; 28784b72b91aSCy Schubert struct crypto_ec_key *own_key; 287985732ac8SCy Schubert 2880c1d255d3SCy Schubert own_key = auth->own_protocol_key; 2881c1d255d3SCy Schubert #ifdef CONFIG_DPP2 2882c1d255d3SCy Schubert if (auth->reconfig_connector_key == DPP_CONFIG_REUSEKEY && 2883c1d255d3SCy Schubert auth->reconfig_old_protocol_key) 2884c1d255d3SCy Schubert own_key = auth->reconfig_old_protocol_key; 2885c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 28864b72b91aSCy Schubert 28874b72b91aSCy Schubert net_access_key = crypto_ec_key_get_ecprivate_key(own_key, true); 28884b72b91aSCy Schubert if (!net_access_key) 288985732ac8SCy Schubert return; 289085732ac8SCy Schubert 289185732ac8SCy Schubert wpabuf_free(auth->net_access_key); 28924b72b91aSCy Schubert auth->net_access_key = net_access_key; 289385732ac8SCy Schubert } 289485732ac8SCy Schubert 289585732ac8SCy Schubert 289685732ac8SCy Schubert static int dpp_parse_cred_dpp(struct dpp_authentication *auth, 2897c1d255d3SCy Schubert struct dpp_config_obj *conf, 289885732ac8SCy Schubert struct json_token *cred) 289985732ac8SCy Schubert { 290085732ac8SCy Schubert struct dpp_signed_connector_info info; 2901c1d255d3SCy Schubert struct json_token *token, *csign, *ppkey; 290285732ac8SCy Schubert int ret = -1; 29034b72b91aSCy Schubert struct crypto_ec_key *csign_pub = NULL, *pp_pub = NULL; 2904c1d255d3SCy Schubert const struct dpp_curve_params *key_curve = NULL, *pp_curve = NULL; 290585732ac8SCy Schubert const char *signed_connector; 290685732ac8SCy Schubert 290785732ac8SCy Schubert os_memset(&info, 0, sizeof(info)); 290885732ac8SCy Schubert 2909c1d255d3SCy Schubert if (dpp_akm_psk(conf->akm) || dpp_akm_sae(conf->akm)) { 29104bc52338SCy Schubert wpa_printf(MSG_DEBUG, 29114bc52338SCy Schubert "DPP: Legacy credential included in Connector credential"); 2912c1d255d3SCy Schubert if (dpp_parse_cred_legacy(conf, cred) < 0) 29134bc52338SCy Schubert return -1; 29144bc52338SCy Schubert } 29154bc52338SCy Schubert 291685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Connector credential"); 291785732ac8SCy Schubert 291885732ac8SCy Schubert csign = json_get_member(cred, "csign"); 291985732ac8SCy Schubert if (!csign || csign->type != JSON_OBJECT) { 292085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON"); 292185732ac8SCy Schubert goto fail; 292285732ac8SCy Schubert } 292385732ac8SCy Schubert 292485732ac8SCy Schubert csign_pub = dpp_parse_jwk(csign, &key_curve); 292585732ac8SCy Schubert if (!csign_pub) { 292685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK"); 292785732ac8SCy Schubert goto fail; 292885732ac8SCy Schubert } 292985732ac8SCy Schubert dpp_debug_print_key("DPP: Received C-sign-key", csign_pub); 293085732ac8SCy Schubert 2931c1d255d3SCy Schubert ppkey = json_get_member(cred, "ppKey"); 2932c1d255d3SCy Schubert if (ppkey && ppkey->type == JSON_OBJECT) { 2933c1d255d3SCy Schubert pp_pub = dpp_parse_jwk(ppkey, &pp_curve); 2934c1d255d3SCy Schubert if (!pp_pub) { 2935c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to parse ppKey JWK"); 2936c1d255d3SCy Schubert goto fail; 2937c1d255d3SCy Schubert } 2938c1d255d3SCy Schubert dpp_debug_print_key("DPP: Received ppKey", pp_pub); 2939c1d255d3SCy Schubert if (key_curve != pp_curve) { 2940c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 2941c1d255d3SCy Schubert "DPP: C-sign-key and ppKey do not use the same curve"); 2942c1d255d3SCy Schubert goto fail; 2943c1d255d3SCy Schubert } 2944c1d255d3SCy Schubert } 2945c1d255d3SCy Schubert 294685732ac8SCy Schubert token = json_get_member(cred, "signedConnector"); 294785732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 294885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found"); 294985732ac8SCy Schubert goto fail; 295085732ac8SCy Schubert } 295185732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector", 295285732ac8SCy Schubert token->string, os_strlen(token->string)); 295385732ac8SCy Schubert signed_connector = token->string; 295485732ac8SCy Schubert 295585732ac8SCy Schubert if (os_strchr(signed_connector, '"') || 295685732ac8SCy Schubert os_strchr(signed_connector, '\n')) { 295785732ac8SCy Schubert wpa_printf(MSG_DEBUG, 295885732ac8SCy Schubert "DPP: Unexpected character in signedConnector"); 295985732ac8SCy Schubert goto fail; 296085732ac8SCy Schubert } 296185732ac8SCy Schubert 296285732ac8SCy Schubert if (dpp_process_signed_connector(&info, csign_pub, 296385732ac8SCy Schubert signed_connector) != DPP_STATUS_OK) 296485732ac8SCy Schubert goto fail; 296585732ac8SCy Schubert 2966c1d255d3SCy Schubert if (dpp_parse_connector(auth, conf, 2967c1d255d3SCy Schubert info.payload, info.payload_len) < 0) { 296885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector"); 296985732ac8SCy Schubert goto fail; 297085732ac8SCy Schubert } 297185732ac8SCy Schubert 2972c1d255d3SCy Schubert os_free(conf->connector); 2973c1d255d3SCy Schubert conf->connector = os_strdup(signed_connector); 297485732ac8SCy Schubert 2975c1d255d3SCy Schubert dpp_copy_csign(conf, csign_pub); 2976c1d255d3SCy Schubert if (pp_pub) 2977c1d255d3SCy Schubert dpp_copy_ppkey(conf, pp_pub); 2978c1d255d3SCy Schubert if (dpp_akm_dpp(conf->akm) || auth->peer_version >= 2) 2979c1d255d3SCy Schubert dpp_copy_netaccesskey(auth, conf); 298085732ac8SCy Schubert 298185732ac8SCy Schubert ret = 0; 298285732ac8SCy Schubert fail: 29834b72b91aSCy Schubert crypto_ec_key_deinit(csign_pub); 29844b72b91aSCy Schubert crypto_ec_key_deinit(pp_pub); 298585732ac8SCy Schubert os_free(info.payload); 298685732ac8SCy Schubert return ret; 298785732ac8SCy Schubert } 298885732ac8SCy Schubert 298985732ac8SCy Schubert 2990c1d255d3SCy Schubert #ifdef CONFIG_DPP2 2991c1d255d3SCy Schubert static int dpp_parse_cred_dot1x(struct dpp_authentication *auth, 2992c1d255d3SCy Schubert struct dpp_config_obj *conf, 2993c1d255d3SCy Schubert struct json_token *cred) 2994c1d255d3SCy Schubert { 2995c1d255d3SCy Schubert struct json_token *ent, *name; 2996c1d255d3SCy Schubert 2997c1d255d3SCy Schubert ent = json_get_member(cred, "entCreds"); 2998c1d255d3SCy Schubert if (!ent || ent->type != JSON_OBJECT) { 2999c1d255d3SCy Schubert dpp_auth_fail(auth, "No entCreds in JSON"); 3000c1d255d3SCy Schubert return -1; 3001c1d255d3SCy Schubert } 3002c1d255d3SCy Schubert 3003c1d255d3SCy Schubert conf->certbag = json_get_member_base64(ent, "certBag"); 3004c1d255d3SCy Schubert if (!conf->certbag) { 3005c1d255d3SCy Schubert dpp_auth_fail(auth, "No certBag in JSON"); 3006c1d255d3SCy Schubert return -1; 3007c1d255d3SCy Schubert } 3008c1d255d3SCy Schubert wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received certBag", conf->certbag); 30094b72b91aSCy Schubert conf->certs = crypto_pkcs7_get_certificates(conf->certbag); 3010c1d255d3SCy Schubert if (!conf->certs) { 3011c1d255d3SCy Schubert dpp_auth_fail(auth, "No certificates in certBag"); 3012c1d255d3SCy Schubert return -1; 3013c1d255d3SCy Schubert } 3014c1d255d3SCy Schubert 3015c1d255d3SCy Schubert conf->cacert = json_get_member_base64(ent, "caCert"); 3016c1d255d3SCy Schubert if (conf->cacert) 3017c1d255d3SCy Schubert wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received caCert", 3018c1d255d3SCy Schubert conf->cacert); 3019c1d255d3SCy Schubert 3020c1d255d3SCy Schubert name = json_get_member(ent, "trustedEapServerName"); 3021c1d255d3SCy Schubert if (name && 3022c1d255d3SCy Schubert (name->type != JSON_STRING || 3023c1d255d3SCy Schubert has_ctrl_char((const u8 *) name->string, 3024c1d255d3SCy Schubert os_strlen(name->string)))) { 3025c1d255d3SCy Schubert dpp_auth_fail(auth, 3026c1d255d3SCy Schubert "Invalid trustedEapServerName type in JSON"); 3027c1d255d3SCy Schubert return -1; 3028c1d255d3SCy Schubert } 3029c1d255d3SCy Schubert if (name && name->string) { 3030c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Received trustedEapServerName: %s", 3031c1d255d3SCy Schubert name->string); 3032c1d255d3SCy Schubert conf->server_name = os_strdup(name->string); 3033c1d255d3SCy Schubert if (!conf->server_name) 3034c1d255d3SCy Schubert return -1; 3035c1d255d3SCy Schubert } 3036c1d255d3SCy Schubert 3037c1d255d3SCy Schubert return 0; 3038c1d255d3SCy Schubert } 3039c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 3040c1d255d3SCy Schubert 3041c1d255d3SCy Schubert 304285732ac8SCy Schubert const char * dpp_akm_str(enum dpp_akm akm) 304385732ac8SCy Schubert { 304485732ac8SCy Schubert switch (akm) { 304585732ac8SCy Schubert case DPP_AKM_DPP: 304685732ac8SCy Schubert return "dpp"; 304785732ac8SCy Schubert case DPP_AKM_PSK: 304885732ac8SCy Schubert return "psk"; 304985732ac8SCy Schubert case DPP_AKM_SAE: 305085732ac8SCy Schubert return "sae"; 305185732ac8SCy Schubert case DPP_AKM_PSK_SAE: 305285732ac8SCy Schubert return "psk+sae"; 30534bc52338SCy Schubert case DPP_AKM_SAE_DPP: 30544bc52338SCy Schubert return "dpp+sae"; 30554bc52338SCy Schubert case DPP_AKM_PSK_SAE_DPP: 30564bc52338SCy Schubert return "dpp+psk+sae"; 3057c1d255d3SCy Schubert case DPP_AKM_DOT1X: 3058c1d255d3SCy Schubert return "dot1x"; 3059c1d255d3SCy Schubert default: 3060c1d255d3SCy Schubert return "??"; 3061c1d255d3SCy Schubert } 3062c1d255d3SCy Schubert } 3063c1d255d3SCy Schubert 3064c1d255d3SCy Schubert 3065c1d255d3SCy Schubert const char * dpp_akm_selector_str(enum dpp_akm akm) 3066c1d255d3SCy Schubert { 3067c1d255d3SCy Schubert switch (akm) { 3068c1d255d3SCy Schubert case DPP_AKM_DPP: 3069c1d255d3SCy Schubert return "506F9A02"; 3070c1d255d3SCy Schubert case DPP_AKM_PSK: 3071c1d255d3SCy Schubert return "000FAC02+000FAC06"; 3072c1d255d3SCy Schubert case DPP_AKM_SAE: 3073c1d255d3SCy Schubert return "000FAC08"; 3074c1d255d3SCy Schubert case DPP_AKM_PSK_SAE: 3075c1d255d3SCy Schubert return "000FAC02+000FAC06+000FAC08"; 3076c1d255d3SCy Schubert case DPP_AKM_SAE_DPP: 3077c1d255d3SCy Schubert return "506F9A02+000FAC08"; 3078c1d255d3SCy Schubert case DPP_AKM_PSK_SAE_DPP: 3079c1d255d3SCy Schubert return "506F9A02+000FAC08+000FAC02+000FAC06"; 3080c1d255d3SCy Schubert case DPP_AKM_DOT1X: 3081c1d255d3SCy Schubert return "000FAC01+000FAC05"; 308285732ac8SCy Schubert default: 308385732ac8SCy Schubert return "??"; 308485732ac8SCy Schubert } 308585732ac8SCy Schubert } 308685732ac8SCy Schubert 308785732ac8SCy Schubert 308885732ac8SCy Schubert static enum dpp_akm dpp_akm_from_str(const char *akm) 308985732ac8SCy Schubert { 3090c1d255d3SCy Schubert const char *pos; 3091c1d255d3SCy Schubert int dpp = 0, psk = 0, sae = 0, dot1x = 0; 3092c1d255d3SCy Schubert 309385732ac8SCy Schubert if (os_strcmp(akm, "psk") == 0) 309485732ac8SCy Schubert return DPP_AKM_PSK; 309585732ac8SCy Schubert if (os_strcmp(akm, "sae") == 0) 309685732ac8SCy Schubert return DPP_AKM_SAE; 309785732ac8SCy Schubert if (os_strcmp(akm, "psk+sae") == 0) 309885732ac8SCy Schubert return DPP_AKM_PSK_SAE; 309985732ac8SCy Schubert if (os_strcmp(akm, "dpp") == 0) 310085732ac8SCy Schubert return DPP_AKM_DPP; 31014bc52338SCy Schubert if (os_strcmp(akm, "dpp+sae") == 0) 31024bc52338SCy Schubert return DPP_AKM_SAE_DPP; 31034bc52338SCy Schubert if (os_strcmp(akm, "dpp+psk+sae") == 0) 31044bc52338SCy Schubert return DPP_AKM_PSK_SAE_DPP; 3105c1d255d3SCy Schubert if (os_strcmp(akm, "dot1x") == 0) 3106c1d255d3SCy Schubert return DPP_AKM_DOT1X; 3107c1d255d3SCy Schubert 3108c1d255d3SCy Schubert pos = akm; 3109c1d255d3SCy Schubert while (*pos) { 3110c1d255d3SCy Schubert if (os_strlen(pos) < 8) 3111c1d255d3SCy Schubert break; 3112c1d255d3SCy Schubert if (os_strncasecmp(pos, "506F9A02", 8) == 0) 3113c1d255d3SCy Schubert dpp = 1; 3114c1d255d3SCy Schubert else if (os_strncasecmp(pos, "000FAC02", 8) == 0) 3115c1d255d3SCy Schubert psk = 1; 3116c1d255d3SCy Schubert else if (os_strncasecmp(pos, "000FAC06", 8) == 0) 3117c1d255d3SCy Schubert psk = 1; 3118c1d255d3SCy Schubert else if (os_strncasecmp(pos, "000FAC08", 8) == 0) 3119c1d255d3SCy Schubert sae = 1; 3120c1d255d3SCy Schubert else if (os_strncasecmp(pos, "000FAC01", 8) == 0) 3121c1d255d3SCy Schubert dot1x = 1; 3122c1d255d3SCy Schubert else if (os_strncasecmp(pos, "000FAC05", 8) == 0) 3123c1d255d3SCy Schubert dot1x = 1; 3124c1d255d3SCy Schubert pos += 8; 3125c1d255d3SCy Schubert if (*pos != '+') 3126c1d255d3SCy Schubert break; 3127c1d255d3SCy Schubert pos++; 3128c1d255d3SCy Schubert } 3129c1d255d3SCy Schubert 3130c1d255d3SCy Schubert if (dpp && psk && sae) 3131c1d255d3SCy Schubert return DPP_AKM_PSK_SAE_DPP; 3132c1d255d3SCy Schubert if (dpp && sae) 3133c1d255d3SCy Schubert return DPP_AKM_SAE_DPP; 3134c1d255d3SCy Schubert if (dpp) 3135c1d255d3SCy Schubert return DPP_AKM_DPP; 3136c1d255d3SCy Schubert if (psk && sae) 3137c1d255d3SCy Schubert return DPP_AKM_PSK_SAE; 3138c1d255d3SCy Schubert if (sae) 3139c1d255d3SCy Schubert return DPP_AKM_SAE; 3140c1d255d3SCy Schubert if (psk) 3141c1d255d3SCy Schubert return DPP_AKM_PSK; 3142c1d255d3SCy Schubert if (dot1x) 3143c1d255d3SCy Schubert return DPP_AKM_DOT1X; 3144c1d255d3SCy Schubert 314585732ac8SCy Schubert return DPP_AKM_UNKNOWN; 314685732ac8SCy Schubert } 314785732ac8SCy Schubert 314885732ac8SCy Schubert 314985732ac8SCy Schubert static int dpp_parse_conf_obj(struct dpp_authentication *auth, 315085732ac8SCy Schubert const u8 *conf_obj, u16 conf_obj_len) 315185732ac8SCy Schubert { 315285732ac8SCy Schubert int ret = -1; 315385732ac8SCy Schubert struct json_token *root, *token, *discovery, *cred; 3154c1d255d3SCy Schubert struct dpp_config_obj *conf; 3155c1d255d3SCy Schubert struct wpabuf *ssid64 = NULL; 3156c1d255d3SCy Schubert int legacy; 315785732ac8SCy Schubert 315885732ac8SCy Schubert root = json_parse((const char *) conf_obj, conf_obj_len); 315985732ac8SCy Schubert if (!root) 316085732ac8SCy Schubert return -1; 316185732ac8SCy Schubert if (root->type != JSON_OBJECT) { 316285732ac8SCy Schubert dpp_auth_fail(auth, "JSON root is not an object"); 316385732ac8SCy Schubert goto fail; 316485732ac8SCy Schubert } 316585732ac8SCy Schubert 316685732ac8SCy Schubert token = json_get_member(root, "wi-fi_tech"); 316785732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 316885732ac8SCy Schubert dpp_auth_fail(auth, "No wi-fi_tech string value found"); 316985732ac8SCy Schubert goto fail; 317085732ac8SCy Schubert } 317185732ac8SCy Schubert if (os_strcmp(token->string, "infra") != 0) { 317285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'", 317385732ac8SCy Schubert token->string); 317485732ac8SCy Schubert dpp_auth_fail(auth, "Unsupported wi-fi_tech value"); 317585732ac8SCy Schubert goto fail; 317685732ac8SCy Schubert } 317785732ac8SCy Schubert 317885732ac8SCy Schubert discovery = json_get_member(root, "discovery"); 317985732ac8SCy Schubert if (!discovery || discovery->type != JSON_OBJECT) { 318085732ac8SCy Schubert dpp_auth_fail(auth, "No discovery object in JSON"); 318185732ac8SCy Schubert goto fail; 318285732ac8SCy Schubert } 318385732ac8SCy Schubert 3184c1d255d3SCy Schubert ssid64 = json_get_member_base64url(discovery, "ssid64"); 3185c1d255d3SCy Schubert if (ssid64) { 3186c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid64", 3187c1d255d3SCy Schubert wpabuf_head(ssid64), wpabuf_len(ssid64)); 3188c1d255d3SCy Schubert if (wpabuf_len(ssid64) > SSID_MAX_LEN) { 3189c1d255d3SCy Schubert dpp_auth_fail(auth, "Too long discovery::ssid64 value"); 3190c1d255d3SCy Schubert goto fail; 3191c1d255d3SCy Schubert } 3192c1d255d3SCy Schubert } else { 319385732ac8SCy Schubert token = json_get_member(discovery, "ssid"); 319485732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 3195c1d255d3SCy Schubert dpp_auth_fail(auth, 3196c1d255d3SCy Schubert "No discovery::ssid string value found"); 319785732ac8SCy Schubert goto fail; 319885732ac8SCy Schubert } 319985732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid", 320085732ac8SCy Schubert token->string, os_strlen(token->string)); 320185732ac8SCy Schubert if (os_strlen(token->string) > SSID_MAX_LEN) { 3202c1d255d3SCy Schubert dpp_auth_fail(auth, 3203c1d255d3SCy Schubert "Too long discovery::ssid string value"); 320485732ac8SCy Schubert goto fail; 320585732ac8SCy Schubert } 3206c1d255d3SCy Schubert } 3207c1d255d3SCy Schubert 3208c1d255d3SCy Schubert if (auth->num_conf_obj == DPP_MAX_CONF_OBJ) { 3209c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 3210c1d255d3SCy Schubert "DPP: No room for this many Config Objects - ignore this one"); 3211c1d255d3SCy Schubert ret = 0; 3212c1d255d3SCy Schubert goto fail; 3213c1d255d3SCy Schubert } 3214c1d255d3SCy Schubert conf = &auth->conf_obj[auth->num_conf_obj++]; 3215c1d255d3SCy Schubert 3216c1d255d3SCy Schubert if (ssid64) { 3217c1d255d3SCy Schubert conf->ssid_len = wpabuf_len(ssid64); 3218c1d255d3SCy Schubert os_memcpy(conf->ssid, wpabuf_head(ssid64), conf->ssid_len); 3219c1d255d3SCy Schubert } else { 3220c1d255d3SCy Schubert conf->ssid_len = os_strlen(token->string); 3221c1d255d3SCy Schubert os_memcpy(conf->ssid, token->string, conf->ssid_len); 3222c1d255d3SCy Schubert } 3223c1d255d3SCy Schubert 3224c1d255d3SCy Schubert token = json_get_member(discovery, "ssid_charset"); 3225c1d255d3SCy Schubert if (token && token->type == JSON_NUMBER) { 3226c1d255d3SCy Schubert conf->ssid_charset = token->number; 3227c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: ssid_charset=%d", 3228c1d255d3SCy Schubert conf->ssid_charset); 3229c1d255d3SCy Schubert } 323085732ac8SCy Schubert 323185732ac8SCy Schubert cred = json_get_member(root, "cred"); 323285732ac8SCy Schubert if (!cred || cred->type != JSON_OBJECT) { 323385732ac8SCy Schubert dpp_auth_fail(auth, "No cred object in JSON"); 323485732ac8SCy Schubert goto fail; 323585732ac8SCy Schubert } 323685732ac8SCy Schubert 323785732ac8SCy Schubert token = json_get_member(cred, "akm"); 323885732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 323985732ac8SCy Schubert dpp_auth_fail(auth, "No cred::akm string value found"); 324085732ac8SCy Schubert goto fail; 324185732ac8SCy Schubert } 3242c1d255d3SCy Schubert conf->akm = dpp_akm_from_str(token->string); 324385732ac8SCy Schubert 3244c1d255d3SCy Schubert legacy = dpp_akm_legacy(conf->akm); 3245c1d255d3SCy Schubert if (legacy && auth->peer_version >= 2) { 3246c1d255d3SCy Schubert struct json_token *csign, *s_conn; 3247c1d255d3SCy Schubert 3248c1d255d3SCy Schubert csign = json_get_member(cred, "csign"); 3249c1d255d3SCy Schubert s_conn = json_get_member(cred, "signedConnector"); 3250c1d255d3SCy Schubert if (csign && csign->type == JSON_OBJECT && 3251c1d255d3SCy Schubert s_conn && s_conn->type == JSON_STRING) 3252c1d255d3SCy Schubert legacy = 0; 3253c1d255d3SCy Schubert } 3254c1d255d3SCy Schubert if (legacy) { 3255c1d255d3SCy Schubert if (dpp_parse_cred_legacy(conf, cred) < 0) 325685732ac8SCy Schubert goto fail; 3257c1d255d3SCy Schubert } else if (dpp_akm_dpp(conf->akm) || 3258c1d255d3SCy Schubert (auth->peer_version >= 2 && dpp_akm_legacy(conf->akm))) { 3259c1d255d3SCy Schubert if (dpp_parse_cred_dpp(auth, conf, cred) < 0) 326085732ac8SCy Schubert goto fail; 3261c1d255d3SCy Schubert #ifdef CONFIG_DPP2 3262c1d255d3SCy Schubert } else if (conf->akm == DPP_AKM_DOT1X) { 3263c1d255d3SCy Schubert if (dpp_parse_cred_dot1x(auth, conf, cred) < 0 || 3264c1d255d3SCy Schubert dpp_parse_cred_dpp(auth, conf, cred) < 0) 3265c1d255d3SCy Schubert goto fail; 3266c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 326785732ac8SCy Schubert } else { 326885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s", 326985732ac8SCy Schubert token->string); 327085732ac8SCy Schubert dpp_auth_fail(auth, "Unsupported akm"); 327185732ac8SCy Schubert goto fail; 327285732ac8SCy Schubert } 327385732ac8SCy Schubert 327485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully"); 327585732ac8SCy Schubert ret = 0; 327685732ac8SCy Schubert fail: 3277c1d255d3SCy Schubert wpabuf_free(ssid64); 327885732ac8SCy Schubert json_free(root); 327985732ac8SCy Schubert return ret; 328085732ac8SCy Schubert } 328185732ac8SCy Schubert 328285732ac8SCy Schubert 3283c1d255d3SCy Schubert #ifdef CONFIG_DPP2 3284c1d255d3SCy Schubert static u8 * dpp_get_csr_attrs(const u8 *attrs, size_t attrs_len, size_t *len) 3285c1d255d3SCy Schubert { 3286c1d255d3SCy Schubert const u8 *b64; 3287c1d255d3SCy Schubert u16 b64_len; 3288c1d255d3SCy Schubert 3289c1d255d3SCy Schubert b64 = dpp_get_attr(attrs, attrs_len, DPP_ATTR_CSR_ATTR_REQ, &b64_len); 3290c1d255d3SCy Schubert if (!b64) 3291c1d255d3SCy Schubert return NULL; 3292c1d255d3SCy Schubert return base64_decode((const char *) b64, b64_len, len); 3293c1d255d3SCy Schubert } 3294c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 3295c1d255d3SCy Schubert 3296c1d255d3SCy Schubert 329785732ac8SCy Schubert int dpp_conf_resp_rx(struct dpp_authentication *auth, 329885732ac8SCy Schubert const struct wpabuf *resp) 329985732ac8SCy Schubert { 330085732ac8SCy Schubert const u8 *wrapped_data, *e_nonce, *status, *conf_obj; 330185732ac8SCy Schubert u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len; 3302c1d255d3SCy Schubert const u8 *env_data; 3303c1d255d3SCy Schubert u16 env_data_len; 330485732ac8SCy Schubert const u8 *addr[1]; 330585732ac8SCy Schubert size_t len[1]; 330685732ac8SCy Schubert u8 *unwrapped = NULL; 330785732ac8SCy Schubert size_t unwrapped_len = 0; 330885732ac8SCy Schubert int ret = -1; 330985732ac8SCy Schubert 33104bc52338SCy Schubert auth->conf_resp_status = 255; 33114bc52338SCy Schubert 331285732ac8SCy Schubert if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { 331385732ac8SCy Schubert dpp_auth_fail(auth, "Invalid attribute in config response"); 331485732ac8SCy Schubert return -1; 331585732ac8SCy Schubert } 331685732ac8SCy Schubert 331785732ac8SCy Schubert wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), 331885732ac8SCy Schubert DPP_ATTR_WRAPPED_DATA, 331985732ac8SCy Schubert &wrapped_data_len); 332085732ac8SCy Schubert if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 332185732ac8SCy Schubert dpp_auth_fail(auth, 332285732ac8SCy Schubert "Missing or invalid required Wrapped Data attribute"); 332385732ac8SCy Schubert return -1; 332485732ac8SCy Schubert } 332585732ac8SCy Schubert 332685732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 332785732ac8SCy Schubert wrapped_data, wrapped_data_len); 332885732ac8SCy Schubert unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 332985732ac8SCy Schubert unwrapped = os_malloc(unwrapped_len); 333085732ac8SCy Schubert if (!unwrapped) 333185732ac8SCy Schubert return -1; 333285732ac8SCy Schubert 333385732ac8SCy Schubert addr[0] = wpabuf_head(resp); 333485732ac8SCy Schubert len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp); 333585732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); 333685732ac8SCy Schubert 333785732ac8SCy Schubert if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 333885732ac8SCy Schubert wrapped_data, wrapped_data_len, 333985732ac8SCy Schubert 1, addr, len, unwrapped) < 0) { 334085732ac8SCy Schubert dpp_auth_fail(auth, "AES-SIV decryption failed"); 334185732ac8SCy Schubert goto fail; 334285732ac8SCy Schubert } 334385732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 334485732ac8SCy Schubert unwrapped, unwrapped_len); 334585732ac8SCy Schubert 334685732ac8SCy Schubert if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 334785732ac8SCy Schubert dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 334885732ac8SCy Schubert goto fail; 334985732ac8SCy Schubert } 335085732ac8SCy Schubert 335185732ac8SCy Schubert e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 335285732ac8SCy Schubert DPP_ATTR_ENROLLEE_NONCE, 335385732ac8SCy Schubert &e_nonce_len); 335485732ac8SCy Schubert if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 335585732ac8SCy Schubert dpp_auth_fail(auth, 335685732ac8SCy Schubert "Missing or invalid Enrollee Nonce attribute"); 335785732ac8SCy Schubert goto fail; 335885732ac8SCy Schubert } 335985732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 336085732ac8SCy Schubert if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { 336185732ac8SCy Schubert dpp_auth_fail(auth, "Enrollee Nonce mismatch"); 336285732ac8SCy Schubert goto fail; 336385732ac8SCy Schubert } 336485732ac8SCy Schubert 336585732ac8SCy Schubert status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), 336685732ac8SCy Schubert DPP_ATTR_STATUS, &status_len); 336785732ac8SCy Schubert if (!status || status_len < 1) { 336885732ac8SCy Schubert dpp_auth_fail(auth, 336985732ac8SCy Schubert "Missing or invalid required DPP Status attribute"); 337085732ac8SCy Schubert goto fail; 337185732ac8SCy Schubert } 33724bc52338SCy Schubert auth->conf_resp_status = status[0]; 337385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); 3374c1d255d3SCy Schubert #ifdef CONFIG_DPP2 3375c1d255d3SCy Schubert if (status[0] == DPP_STATUS_CSR_NEEDED) { 3376c1d255d3SCy Schubert u8 *csrattrs; 3377c1d255d3SCy Schubert size_t csrattrs_len; 3378c1d255d3SCy Schubert 3379c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Configurator requested CSR"); 3380c1d255d3SCy Schubert 3381c1d255d3SCy Schubert csrattrs = dpp_get_csr_attrs(unwrapped, unwrapped_len, 3382c1d255d3SCy Schubert &csrattrs_len); 3383c1d255d3SCy Schubert if (!csrattrs) { 3384c1d255d3SCy Schubert dpp_auth_fail(auth, 3385c1d255d3SCy Schubert "Missing or invalid CSR Attributes Request attribute"); 3386c1d255d3SCy Schubert goto fail; 3387c1d255d3SCy Schubert } 3388c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: CsrAttrs", csrattrs, csrattrs_len); 3389c1d255d3SCy Schubert os_free(auth->csrattrs); 3390c1d255d3SCy Schubert auth->csrattrs = csrattrs; 3391c1d255d3SCy Schubert auth->csrattrs_len = csrattrs_len; 3392c1d255d3SCy Schubert ret = -2; 3393c1d255d3SCy Schubert goto fail; 3394c1d255d3SCy Schubert } 3395c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 3396*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 3397*a90b9d01SCy Schubert if (status[0] == DPP_STATUS_NEW_KEY_NEEDED) { 3398*a90b9d01SCy Schubert const u8 *fcgroup, *r_proto; 3399*a90b9d01SCy Schubert u16 fcgroup_len, r_proto_len; 3400*a90b9d01SCy Schubert u16 group; 3401*a90b9d01SCy Schubert const struct dpp_curve_params *curve; 3402*a90b9d01SCy Schubert struct crypto_ec_key *new_pe; 3403*a90b9d01SCy Schubert struct crypto_ec_key *pc; 3404*a90b9d01SCy Schubert 3405*a90b9d01SCy Schubert fcgroup = dpp_get_attr(unwrapped, unwrapped_len, 3406*a90b9d01SCy Schubert DPP_ATTR_FINITE_CYCLIC_GROUP, 3407*a90b9d01SCy Schubert &fcgroup_len); 3408*a90b9d01SCy Schubert if (!fcgroup || fcgroup_len != 2) { 3409*a90b9d01SCy Schubert dpp_auth_fail(auth, 3410*a90b9d01SCy Schubert "Missing or invalid required Finite Cyclic Group attribute"); 3411*a90b9d01SCy Schubert goto fail; 3412*a90b9d01SCy Schubert } 3413*a90b9d01SCy Schubert group = WPA_GET_LE16(fcgroup); 3414*a90b9d01SCy Schubert 3415*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 3416*a90b9d01SCy Schubert "DPP: Configurator requested a new protocol key from group %u", 3417*a90b9d01SCy Schubert group); 3418*a90b9d01SCy Schubert curve = dpp_get_curve_ike_group(group); 3419*a90b9d01SCy Schubert if (!curve) { 3420*a90b9d01SCy Schubert dpp_auth_fail(auth, 3421*a90b9d01SCy Schubert "Unsupported group for new protocol key"); 3422*a90b9d01SCy Schubert goto fail; 3423*a90b9d01SCy Schubert } 3424*a90b9d01SCy Schubert 3425*a90b9d01SCy Schubert new_pe = dpp_gen_keypair(curve); 3426*a90b9d01SCy Schubert if (!new_pe) { 3427*a90b9d01SCy Schubert dpp_auth_fail(auth, 3428*a90b9d01SCy Schubert "Failed to generate a new protocol key"); 3429*a90b9d01SCy Schubert goto fail; 3430*a90b9d01SCy Schubert } 3431*a90b9d01SCy Schubert 3432*a90b9d01SCy Schubert crypto_ec_key_deinit(auth->own_protocol_key); 3433*a90b9d01SCy Schubert auth->own_protocol_key = new_pe; 3434*a90b9d01SCy Schubert auth->new_curve = curve; 3435*a90b9d01SCy Schubert 3436*a90b9d01SCy Schubert r_proto = dpp_get_attr(unwrapped, unwrapped_len, 3437*a90b9d01SCy Schubert DPP_ATTR_R_PROTOCOL_KEY, 3438*a90b9d01SCy Schubert &r_proto_len); 3439*a90b9d01SCy Schubert if (!r_proto) { 3440*a90b9d01SCy Schubert dpp_auth_fail(auth, 3441*a90b9d01SCy Schubert "Missing required Responder Protocol Key attribute (Pc)"); 3442*a90b9d01SCy Schubert goto fail; 3443*a90b9d01SCy Schubert } 3444*a90b9d01SCy Schubert wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key (new Pc)", 3445*a90b9d01SCy Schubert r_proto, r_proto_len); 3446*a90b9d01SCy Schubert 3447*a90b9d01SCy Schubert pc = dpp_set_pubkey_point(new_pe, r_proto, r_proto_len); 3448*a90b9d01SCy Schubert if (!pc) { 3449*a90b9d01SCy Schubert dpp_auth_fail(auth, "Invalid Responder Protocol Key (Pc)"); 3450*a90b9d01SCy Schubert goto fail; 3451*a90b9d01SCy Schubert } 3452*a90b9d01SCy Schubert dpp_debug_print_key("New Peer Protocol Key (Pc)", pc); 3453*a90b9d01SCy Schubert 3454*a90b9d01SCy Schubert crypto_ec_key_deinit(auth->peer_protocol_key); 3455*a90b9d01SCy Schubert auth->peer_protocol_key = pc; 3456*a90b9d01SCy Schubert 3457*a90b9d01SCy Schubert auth->waiting_new_key = true; 3458*a90b9d01SCy Schubert ret = -3; 3459*a90b9d01SCy Schubert goto fail; 3460*a90b9d01SCy Schubert } 3461*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 346285732ac8SCy Schubert if (status[0] != DPP_STATUS_OK) { 346385732ac8SCy Schubert dpp_auth_fail(auth, "Configurator rejected configuration"); 346485732ac8SCy Schubert goto fail; 346585732ac8SCy Schubert } 346685732ac8SCy Schubert 3467c1d255d3SCy Schubert env_data = dpp_get_attr(unwrapped, unwrapped_len, 3468c1d255d3SCy Schubert DPP_ATTR_ENVELOPED_DATA, &env_data_len); 3469c1d255d3SCy Schubert #ifdef CONFIG_DPP2 3470c1d255d3SCy Schubert if (env_data && 3471c1d255d3SCy Schubert dpp_conf_resp_env_data(auth, env_data, env_data_len) < 0) 3472c1d255d3SCy Schubert goto fail; 3473c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 3474c1d255d3SCy Schubert 3475c1d255d3SCy Schubert conf_obj = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_OBJ, 3476c1d255d3SCy Schubert &conf_obj_len); 3477c1d255d3SCy Schubert if (!conf_obj && !env_data) { 347885732ac8SCy Schubert dpp_auth_fail(auth, 347985732ac8SCy Schubert "Missing required Configuration Object attribute"); 348085732ac8SCy Schubert goto fail; 348185732ac8SCy Schubert } 3482c1d255d3SCy Schubert while (conf_obj) { 348385732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", 348485732ac8SCy Schubert conf_obj, conf_obj_len); 348585732ac8SCy Schubert if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0) 348685732ac8SCy Schubert goto fail; 3487c1d255d3SCy Schubert conf_obj = dpp_get_attr_next(conf_obj, unwrapped, unwrapped_len, 3488c1d255d3SCy Schubert DPP_ATTR_CONFIG_OBJ, 3489c1d255d3SCy Schubert &conf_obj_len); 3490c1d255d3SCy Schubert } 3491c1d255d3SCy Schubert 3492c1d255d3SCy Schubert #ifdef CONFIG_DPP2 3493c1d255d3SCy Schubert status = dpp_get_attr(unwrapped, unwrapped_len, 3494c1d255d3SCy Schubert DPP_ATTR_SEND_CONN_STATUS, &status_len); 3495c1d255d3SCy Schubert if (status) { 3496c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 3497c1d255d3SCy Schubert "DPP: Configurator requested connection status result"); 3498c1d255d3SCy Schubert auth->conn_status_requested = 1; 3499c1d255d3SCy Schubert } 3500c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 350185732ac8SCy Schubert 350285732ac8SCy Schubert ret = 0; 350385732ac8SCy Schubert 350485732ac8SCy Schubert fail: 350585732ac8SCy Schubert os_free(unwrapped); 350685732ac8SCy Schubert return ret; 350785732ac8SCy Schubert } 350885732ac8SCy Schubert 350985732ac8SCy Schubert 35104bc52338SCy Schubert #ifdef CONFIG_DPP2 3511c1d255d3SCy Schubert 35124bc52338SCy Schubert enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, 35134bc52338SCy Schubert const u8 *hdr, 35144bc52338SCy Schubert const u8 *attr_start, size_t attr_len) 35154bc52338SCy Schubert { 35164bc52338SCy Schubert const u8 *wrapped_data, *status, *e_nonce; 35174bc52338SCy Schubert u16 wrapped_data_len, status_len, e_nonce_len; 35184bc52338SCy Schubert const u8 *addr[2]; 35194bc52338SCy Schubert size_t len[2]; 35204bc52338SCy Schubert u8 *unwrapped = NULL; 35214bc52338SCy Schubert size_t unwrapped_len = 0; 35224bc52338SCy Schubert enum dpp_status_error ret = 256; 35234bc52338SCy Schubert 35244bc52338SCy Schubert wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 35254bc52338SCy Schubert &wrapped_data_len); 35264bc52338SCy Schubert if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 35274bc52338SCy Schubert dpp_auth_fail(auth, 35284bc52338SCy Schubert "Missing or invalid required Wrapped Data attribute"); 35294bc52338SCy Schubert goto fail; 35304bc52338SCy Schubert } 35314bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", 35324bc52338SCy Schubert wrapped_data, wrapped_data_len); 35334bc52338SCy Schubert 35344bc52338SCy Schubert attr_len = wrapped_data - 4 - attr_start; 35354bc52338SCy Schubert 35364bc52338SCy Schubert addr[0] = hdr; 35374bc52338SCy Schubert len[0] = DPP_HDR_LEN; 35384bc52338SCy Schubert addr[1] = attr_start; 35394bc52338SCy Schubert len[1] = attr_len; 35404bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 35414bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 35424bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 35434bc52338SCy Schubert wrapped_data, wrapped_data_len); 35444bc52338SCy Schubert unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 35454bc52338SCy Schubert unwrapped = os_malloc(unwrapped_len); 35464bc52338SCy Schubert if (!unwrapped) 35474bc52338SCy Schubert goto fail; 35484bc52338SCy Schubert if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 35494bc52338SCy Schubert wrapped_data, wrapped_data_len, 35504bc52338SCy Schubert 2, addr, len, unwrapped) < 0) { 35514bc52338SCy Schubert dpp_auth_fail(auth, "AES-SIV decryption failed"); 35524bc52338SCy Schubert goto fail; 35534bc52338SCy Schubert } 35544bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 35554bc52338SCy Schubert unwrapped, unwrapped_len); 35564bc52338SCy Schubert 35574bc52338SCy Schubert if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 35584bc52338SCy Schubert dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 35594bc52338SCy Schubert goto fail; 35604bc52338SCy Schubert } 35614bc52338SCy Schubert 35624bc52338SCy Schubert e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 35634bc52338SCy Schubert DPP_ATTR_ENROLLEE_NONCE, 35644bc52338SCy Schubert &e_nonce_len); 35654bc52338SCy Schubert if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 35664bc52338SCy Schubert dpp_auth_fail(auth, 35674bc52338SCy Schubert "Missing or invalid Enrollee Nonce attribute"); 35684bc52338SCy Schubert goto fail; 35694bc52338SCy Schubert } 35704bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 35714bc52338SCy Schubert if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { 35724bc52338SCy Schubert dpp_auth_fail(auth, "Enrollee Nonce mismatch"); 35734bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", 35744bc52338SCy Schubert auth->e_nonce, e_nonce_len); 35754bc52338SCy Schubert goto fail; 35764bc52338SCy Schubert } 35774bc52338SCy Schubert 35784bc52338SCy Schubert status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS, 35794bc52338SCy Schubert &status_len); 35804bc52338SCy Schubert if (!status || status_len < 1) { 35814bc52338SCy Schubert dpp_auth_fail(auth, 35824bc52338SCy Schubert "Missing or invalid required DPP Status attribute"); 35834bc52338SCy Schubert goto fail; 35844bc52338SCy Schubert } 35854bc52338SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); 35864bc52338SCy Schubert ret = status[0]; 35874bc52338SCy Schubert 35884bc52338SCy Schubert fail: 35894bc52338SCy Schubert bin_clear_free(unwrapped, unwrapped_len); 35904bc52338SCy Schubert return ret; 35914bc52338SCy Schubert } 35924bc52338SCy Schubert 35934bc52338SCy Schubert 35944bc52338SCy Schubert struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, 35954bc52338SCy Schubert enum dpp_status_error status) 35964bc52338SCy Schubert { 35974bc52338SCy Schubert struct wpabuf *msg, *clear; 35984bc52338SCy Schubert size_t nonce_len, clear_len, attr_len; 35994bc52338SCy Schubert const u8 *addr[2]; 36004bc52338SCy Schubert size_t len[2]; 36014bc52338SCy Schubert u8 *wrapped; 36024bc52338SCy Schubert 36034bc52338SCy Schubert nonce_len = auth->curve->nonce_len; 36044bc52338SCy Schubert clear_len = 5 + 4 + nonce_len; 36054bc52338SCy Schubert attr_len = 4 + clear_len + AES_BLOCK_SIZE; 36064bc52338SCy Schubert clear = wpabuf_alloc(clear_len); 36074bc52338SCy Schubert msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len); 36084bc52338SCy Schubert if (!clear || !msg) 3609c1d255d3SCy Schubert goto fail; 36104bc52338SCy Schubert 36114bc52338SCy Schubert /* DPP Status */ 36124bc52338SCy Schubert dpp_build_attr_status(clear, status); 36134bc52338SCy Schubert 36144bc52338SCy Schubert /* E-nonce */ 36154bc52338SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 36164bc52338SCy Schubert wpabuf_put_le16(clear, nonce_len); 36174bc52338SCy Schubert wpabuf_put_data(clear, auth->e_nonce, nonce_len); 36184bc52338SCy Schubert 36194bc52338SCy Schubert /* OUI, OUI type, Crypto Suite, DPP frame type */ 36204bc52338SCy Schubert addr[0] = wpabuf_head_u8(msg) + 2; 36214bc52338SCy Schubert len[0] = 3 + 1 + 1 + 1; 36224bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 36234bc52338SCy Schubert 36244bc52338SCy Schubert /* Attributes before Wrapped Data (none) */ 36254bc52338SCy Schubert addr[1] = wpabuf_put(msg, 0); 36264bc52338SCy Schubert len[1] = 0; 36274bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 36284bc52338SCy Schubert 36294bc52338SCy Schubert /* Wrapped Data */ 36304bc52338SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 36314bc52338SCy Schubert wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 36324bc52338SCy Schubert wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 36334bc52338SCy Schubert 36344bc52338SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 36354bc52338SCy Schubert if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 36364bc52338SCy Schubert wpabuf_head(clear), wpabuf_len(clear), 36374bc52338SCy Schubert 2, addr, len, wrapped) < 0) 36384bc52338SCy Schubert goto fail; 36394bc52338SCy Schubert 36404bc52338SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg); 36414bc52338SCy Schubert wpabuf_free(clear); 36424bc52338SCy Schubert return msg; 36434bc52338SCy Schubert fail: 36444bc52338SCy Schubert wpabuf_free(clear); 36454bc52338SCy Schubert wpabuf_free(msg); 36464bc52338SCy Schubert return NULL; 36474bc52338SCy Schubert } 36484bc52338SCy Schubert 36494bc52338SCy Schubert 3650c1d255d3SCy Schubert static int valid_channel_list(const char *val) 3651c1d255d3SCy Schubert { 3652c1d255d3SCy Schubert while (*val) { 3653c1d255d3SCy Schubert if (!((*val >= '0' && *val <= '9') || 3654c1d255d3SCy Schubert *val == '/' || *val == ',')) 3655c1d255d3SCy Schubert return 0; 3656c1d255d3SCy Schubert val++; 3657c1d255d3SCy Schubert } 3658c1d255d3SCy Schubert 3659c1d255d3SCy Schubert return 1; 3660c1d255d3SCy Schubert } 3661c1d255d3SCy Schubert 3662c1d255d3SCy Schubert 3663c1d255d3SCy Schubert enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth, 3664c1d255d3SCy Schubert const u8 *hdr, 3665c1d255d3SCy Schubert const u8 *attr_start, 3666c1d255d3SCy Schubert size_t attr_len, 3667c1d255d3SCy Schubert u8 *ssid, size_t *ssid_len, 3668c1d255d3SCy Schubert char **channel_list) 3669c1d255d3SCy Schubert { 3670c1d255d3SCy Schubert const u8 *wrapped_data, *status, *e_nonce; 3671c1d255d3SCy Schubert u16 wrapped_data_len, status_len, e_nonce_len; 3672c1d255d3SCy Schubert const u8 *addr[2]; 3673c1d255d3SCy Schubert size_t len[2]; 3674c1d255d3SCy Schubert u8 *unwrapped = NULL; 3675c1d255d3SCy Schubert size_t unwrapped_len = 0; 3676c1d255d3SCy Schubert enum dpp_status_error ret = 256; 3677c1d255d3SCy Schubert struct json_token *root = NULL, *token; 3678c1d255d3SCy Schubert struct wpabuf *ssid64; 3679c1d255d3SCy Schubert 3680c1d255d3SCy Schubert *ssid_len = 0; 3681c1d255d3SCy Schubert *channel_list = NULL; 3682c1d255d3SCy Schubert 3683c1d255d3SCy Schubert wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 3684c1d255d3SCy Schubert &wrapped_data_len); 3685c1d255d3SCy Schubert if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 3686c1d255d3SCy Schubert dpp_auth_fail(auth, 3687c1d255d3SCy Schubert "Missing or invalid required Wrapped Data attribute"); 3688c1d255d3SCy Schubert goto fail; 3689c1d255d3SCy Schubert } 3690c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", 3691c1d255d3SCy Schubert wrapped_data, wrapped_data_len); 3692c1d255d3SCy Schubert 3693c1d255d3SCy Schubert attr_len = wrapped_data - 4 - attr_start; 3694c1d255d3SCy Schubert 3695c1d255d3SCy Schubert addr[0] = hdr; 3696c1d255d3SCy Schubert len[0] = DPP_HDR_LEN; 3697c1d255d3SCy Schubert addr[1] = attr_start; 3698c1d255d3SCy Schubert len[1] = attr_len; 3699c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3700c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3701c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 3702c1d255d3SCy Schubert wrapped_data, wrapped_data_len); 3703c1d255d3SCy Schubert unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 3704c1d255d3SCy Schubert unwrapped = os_malloc(unwrapped_len); 3705c1d255d3SCy Schubert if (!unwrapped) 3706c1d255d3SCy Schubert goto fail; 3707c1d255d3SCy Schubert if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 3708c1d255d3SCy Schubert wrapped_data, wrapped_data_len, 3709c1d255d3SCy Schubert 2, addr, len, unwrapped) < 0) { 3710c1d255d3SCy Schubert dpp_auth_fail(auth, "AES-SIV decryption failed"); 3711c1d255d3SCy Schubert goto fail; 3712c1d255d3SCy Schubert } 3713c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 3714c1d255d3SCy Schubert unwrapped, unwrapped_len); 3715c1d255d3SCy Schubert 3716c1d255d3SCy Schubert if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 3717c1d255d3SCy Schubert dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 3718c1d255d3SCy Schubert goto fail; 3719c1d255d3SCy Schubert } 3720c1d255d3SCy Schubert 3721c1d255d3SCy Schubert e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 3722c1d255d3SCy Schubert DPP_ATTR_ENROLLEE_NONCE, 3723c1d255d3SCy Schubert &e_nonce_len); 3724c1d255d3SCy Schubert if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 3725c1d255d3SCy Schubert dpp_auth_fail(auth, 3726c1d255d3SCy Schubert "Missing or invalid Enrollee Nonce attribute"); 3727c1d255d3SCy Schubert goto fail; 3728c1d255d3SCy Schubert } 3729c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 3730c1d255d3SCy Schubert if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { 3731c1d255d3SCy Schubert dpp_auth_fail(auth, "Enrollee Nonce mismatch"); 3732c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", 3733c1d255d3SCy Schubert auth->e_nonce, e_nonce_len); 3734c1d255d3SCy Schubert goto fail; 3735c1d255d3SCy Schubert } 3736c1d255d3SCy Schubert 3737c1d255d3SCy Schubert status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONN_STATUS, 3738c1d255d3SCy Schubert &status_len); 3739c1d255d3SCy Schubert if (!status) { 3740c1d255d3SCy Schubert dpp_auth_fail(auth, 3741c1d255d3SCy Schubert "Missing required DPP Connection Status attribute"); 3742c1d255d3SCy Schubert goto fail; 3743c1d255d3SCy Schubert } 3744c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON", 3745c1d255d3SCy Schubert status, status_len); 3746c1d255d3SCy Schubert 3747c1d255d3SCy Schubert root = json_parse((const char *) status, status_len); 3748c1d255d3SCy Schubert if (!root) { 3749c1d255d3SCy Schubert dpp_auth_fail(auth, "Could not parse connStatus"); 3750c1d255d3SCy Schubert goto fail; 3751c1d255d3SCy Schubert } 3752c1d255d3SCy Schubert 3753c1d255d3SCy Schubert ssid64 = json_get_member_base64url(root, "ssid64"); 3754c1d255d3SCy Schubert if (ssid64 && wpabuf_len(ssid64) <= SSID_MAX_LEN) { 3755c1d255d3SCy Schubert *ssid_len = wpabuf_len(ssid64); 3756c1d255d3SCy Schubert os_memcpy(ssid, wpabuf_head(ssid64), *ssid_len); 3757c1d255d3SCy Schubert } 3758c1d255d3SCy Schubert wpabuf_free(ssid64); 3759c1d255d3SCy Schubert 3760c1d255d3SCy Schubert token = json_get_member(root, "channelList"); 3761c1d255d3SCy Schubert if (token && token->type == JSON_STRING && 3762c1d255d3SCy Schubert valid_channel_list(token->string)) 3763c1d255d3SCy Schubert *channel_list = os_strdup(token->string); 3764c1d255d3SCy Schubert 3765c1d255d3SCy Schubert token = json_get_member(root, "result"); 3766c1d255d3SCy Schubert if (!token || token->type != JSON_NUMBER) { 3767c1d255d3SCy Schubert dpp_auth_fail(auth, "No connStatus - result"); 3768c1d255d3SCy Schubert goto fail; 3769c1d255d3SCy Schubert } 3770c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: result %d", token->number); 3771c1d255d3SCy Schubert ret = token->number; 3772c1d255d3SCy Schubert 3773c1d255d3SCy Schubert fail: 3774c1d255d3SCy Schubert json_free(root); 3775c1d255d3SCy Schubert bin_clear_free(unwrapped, unwrapped_len); 3776c1d255d3SCy Schubert return ret; 3777c1d255d3SCy Schubert } 3778c1d255d3SCy Schubert 3779c1d255d3SCy Schubert 3780c1d255d3SCy Schubert struct wpabuf * dpp_build_conn_status(enum dpp_status_error result, 3781c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len, 3782c1d255d3SCy Schubert const char *channel_list) 3783c1d255d3SCy Schubert { 3784c1d255d3SCy Schubert struct wpabuf *json; 3785c1d255d3SCy Schubert 3786c1d255d3SCy Schubert json = wpabuf_alloc(1000); 3787c1d255d3SCy Schubert if (!json) 3788c1d255d3SCy Schubert return NULL; 3789c1d255d3SCy Schubert json_start_object(json, NULL); 3790c1d255d3SCy Schubert json_add_int(json, "result", result); 3791c1d255d3SCy Schubert if (ssid) { 3792c1d255d3SCy Schubert json_value_sep(json); 3793c1d255d3SCy Schubert if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0) { 3794c1d255d3SCy Schubert wpabuf_free(json); 3795c1d255d3SCy Schubert return NULL; 3796c1d255d3SCy Schubert } 3797c1d255d3SCy Schubert } 3798c1d255d3SCy Schubert if (channel_list) { 3799c1d255d3SCy Schubert json_value_sep(json); 3800c1d255d3SCy Schubert json_add_string(json, "channelList", channel_list); 3801c1d255d3SCy Schubert } 3802c1d255d3SCy Schubert json_end_object(json); 3803c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON", 3804c1d255d3SCy Schubert wpabuf_head(json), wpabuf_len(json)); 3805c1d255d3SCy Schubert 3806c1d255d3SCy Schubert return json; 3807c1d255d3SCy Schubert } 3808c1d255d3SCy Schubert 3809c1d255d3SCy Schubert 3810c1d255d3SCy Schubert struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth, 3811c1d255d3SCy Schubert enum dpp_status_error result, 3812c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len, 3813c1d255d3SCy Schubert const char *channel_list) 3814c1d255d3SCy Schubert { 3815c1d255d3SCy Schubert struct wpabuf *msg = NULL, *clear = NULL, *json; 3816c1d255d3SCy Schubert size_t nonce_len, clear_len, attr_len; 3817c1d255d3SCy Schubert const u8 *addr[2]; 3818c1d255d3SCy Schubert size_t len[2]; 3819c1d255d3SCy Schubert u8 *wrapped; 3820c1d255d3SCy Schubert 3821c1d255d3SCy Schubert json = dpp_build_conn_status(result, ssid, ssid_len, channel_list); 3822c1d255d3SCy Schubert if (!json) 3823c1d255d3SCy Schubert return NULL; 3824c1d255d3SCy Schubert 3825c1d255d3SCy Schubert nonce_len = auth->curve->nonce_len; 3826c1d255d3SCy Schubert clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json); 3827c1d255d3SCy Schubert attr_len = 4 + clear_len + AES_BLOCK_SIZE; 3828c1d255d3SCy Schubert clear = wpabuf_alloc(clear_len); 3829c1d255d3SCy Schubert msg = dpp_alloc_msg(DPP_PA_CONNECTION_STATUS_RESULT, attr_len); 3830c1d255d3SCy Schubert if (!clear || !msg) 3831c1d255d3SCy Schubert goto fail; 3832c1d255d3SCy Schubert 3833c1d255d3SCy Schubert /* E-nonce */ 3834c1d255d3SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 3835c1d255d3SCy Schubert wpabuf_put_le16(clear, nonce_len); 3836c1d255d3SCy Schubert wpabuf_put_data(clear, auth->e_nonce, nonce_len); 3837c1d255d3SCy Schubert 3838c1d255d3SCy Schubert /* DPP Connection Status */ 3839c1d255d3SCy Schubert wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS); 3840c1d255d3SCy Schubert wpabuf_put_le16(clear, wpabuf_len(json)); 3841c1d255d3SCy Schubert wpabuf_put_buf(clear, json); 3842c1d255d3SCy Schubert 3843c1d255d3SCy Schubert /* OUI, OUI type, Crypto Suite, DPP frame type */ 3844c1d255d3SCy Schubert addr[0] = wpabuf_head_u8(msg) + 2; 3845c1d255d3SCy Schubert len[0] = 3 + 1 + 1 + 1; 3846c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3847c1d255d3SCy Schubert 3848c1d255d3SCy Schubert /* Attributes before Wrapped Data (none) */ 3849c1d255d3SCy Schubert addr[1] = wpabuf_put(msg, 0); 3850c1d255d3SCy Schubert len[1] = 0; 3851c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3852c1d255d3SCy Schubert 3853c1d255d3SCy Schubert /* Wrapped Data */ 3854c1d255d3SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 3855c1d255d3SCy Schubert wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 3856c1d255d3SCy Schubert wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 3857c1d255d3SCy Schubert 3858c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 3859c1d255d3SCy Schubert if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 3860c1d255d3SCy Schubert wpabuf_head(clear), wpabuf_len(clear), 3861c1d255d3SCy Schubert 2, addr, len, wrapped) < 0) 3862c1d255d3SCy Schubert goto fail; 3863c1d255d3SCy Schubert 3864c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "DPP: Connection Status Result attributes", 3865c1d255d3SCy Schubert msg); 3866c1d255d3SCy Schubert wpabuf_free(json); 3867c1d255d3SCy Schubert wpabuf_free(clear); 3868c1d255d3SCy Schubert return msg; 3869c1d255d3SCy Schubert fail: 3870c1d255d3SCy Schubert wpabuf_free(json); 3871c1d255d3SCy Schubert wpabuf_free(clear); 3872c1d255d3SCy Schubert wpabuf_free(msg); 3873c1d255d3SCy Schubert return NULL; 3874c1d255d3SCy Schubert } 3875c1d255d3SCy Schubert 3876c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 3877c1d255d3SCy Schubert 3878c1d255d3SCy Schubert 387985732ac8SCy Schubert void dpp_configurator_free(struct dpp_configurator *conf) 388085732ac8SCy Schubert { 388185732ac8SCy Schubert if (!conf) 388285732ac8SCy Schubert return; 38834b72b91aSCy Schubert crypto_ec_key_deinit(conf->csign); 388485732ac8SCy Schubert os_free(conf->kid); 3885c1d255d3SCy Schubert os_free(conf->connector); 38864b72b91aSCy Schubert crypto_ec_key_deinit(conf->connector_key); 38874b72b91aSCy Schubert crypto_ec_key_deinit(conf->pp_key); 388885732ac8SCy Schubert os_free(conf); 388985732ac8SCy Schubert } 389085732ac8SCy Schubert 389185732ac8SCy Schubert 389285732ac8SCy Schubert int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, 389385732ac8SCy Schubert size_t buflen) 389485732ac8SCy Schubert { 38954b72b91aSCy Schubert struct wpabuf *key; 38964b72b91aSCy Schubert int ret = -1; 389785732ac8SCy Schubert 389885732ac8SCy Schubert if (!conf->csign) 389985732ac8SCy Schubert return -1; 390085732ac8SCy Schubert 39014b72b91aSCy Schubert key = crypto_ec_key_get_ecprivate_key(conf->csign, true); 39024b72b91aSCy Schubert if (!key) 390385732ac8SCy Schubert return -1; 390485732ac8SCy Schubert 39054b72b91aSCy Schubert ret = wpa_snprintf_hex(buf, buflen, wpabuf_head(key), wpabuf_len(key)); 390685732ac8SCy Schubert 39074b72b91aSCy Schubert wpabuf_clear_free(key); 390885732ac8SCy Schubert return ret; 390985732ac8SCy Schubert } 391085732ac8SCy Schubert 391185732ac8SCy Schubert 3912c1d255d3SCy Schubert static int dpp_configurator_gen_kid(struct dpp_configurator *conf) 391385732ac8SCy Schubert { 391485732ac8SCy Schubert struct wpabuf *csign_pub = NULL; 391585732ac8SCy Schubert const u8 *addr[1]; 391685732ac8SCy Schubert size_t len[1]; 3917c1d255d3SCy Schubert int res; 391885732ac8SCy Schubert 39194b72b91aSCy Schubert csign_pub = crypto_ec_key_get_pubkey_point(conf->csign, 1); 392085732ac8SCy Schubert if (!csign_pub) { 392185732ac8SCy Schubert wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key"); 3922c1d255d3SCy Schubert return -1; 392385732ac8SCy Schubert } 392485732ac8SCy Schubert 392585732ac8SCy Schubert /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */ 392685732ac8SCy Schubert addr[0] = wpabuf_head(csign_pub); 392785732ac8SCy Schubert len[0] = wpabuf_len(csign_pub); 3928c1d255d3SCy Schubert res = sha256_vector(1, addr, len, conf->kid_hash); 3929c1d255d3SCy Schubert wpabuf_free(csign_pub); 3930c1d255d3SCy Schubert if (res < 0) { 393185732ac8SCy Schubert wpa_printf(MSG_DEBUG, 393285732ac8SCy Schubert "DPP: Failed to derive kid for C-sign-key"); 3933c1d255d3SCy Schubert return -1; 393485732ac8SCy Schubert } 393585732ac8SCy Schubert 3936c1d255d3SCy Schubert conf->kid = base64_url_encode(conf->kid_hash, sizeof(conf->kid_hash), 3937c1d255d3SCy Schubert NULL); 3938c1d255d3SCy Schubert return conf->kid ? 0 : -1; 3939c1d255d3SCy Schubert } 3940c1d255d3SCy Schubert 3941c1d255d3SCy Schubert 3942c1d255d3SCy Schubert static struct dpp_configurator * 3943c1d255d3SCy Schubert dpp_keygen_configurator(const char *curve, const u8 *privkey, 3944c1d255d3SCy Schubert size_t privkey_len, const u8 *pp_key, size_t pp_key_len) 3945c1d255d3SCy Schubert { 3946c1d255d3SCy Schubert struct dpp_configurator *conf; 3947c1d255d3SCy Schubert 3948c1d255d3SCy Schubert conf = os_zalloc(sizeof(*conf)); 3949c1d255d3SCy Schubert if (!conf) 3950c1d255d3SCy Schubert return NULL; 3951c1d255d3SCy Schubert 3952c1d255d3SCy Schubert conf->curve = dpp_get_curve_name(curve); 3953c1d255d3SCy Schubert if (!conf->curve) { 3954c1d255d3SCy Schubert wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve); 3955c1d255d3SCy Schubert os_free(conf); 3956c1d255d3SCy Schubert return NULL; 3957c1d255d3SCy Schubert } 3958c1d255d3SCy Schubert 3959c1d255d3SCy Schubert if (privkey) 3960c1d255d3SCy Schubert conf->csign = dpp_set_keypair(&conf->curve, privkey, 3961c1d255d3SCy Schubert privkey_len); 3962c1d255d3SCy Schubert else 3963c1d255d3SCy Schubert conf->csign = dpp_gen_keypair(conf->curve); 3964c1d255d3SCy Schubert if (pp_key) 3965c1d255d3SCy Schubert conf->pp_key = dpp_set_keypair(&conf->curve, pp_key, 3966c1d255d3SCy Schubert pp_key_len); 3967c1d255d3SCy Schubert else 3968c1d255d3SCy Schubert conf->pp_key = dpp_gen_keypair(conf->curve); 3969c1d255d3SCy Schubert if (!conf->csign || !conf->pp_key) 397085732ac8SCy Schubert goto fail; 3971c1d255d3SCy Schubert conf->own = 1; 3972c1d255d3SCy Schubert 3973c1d255d3SCy Schubert if (dpp_configurator_gen_kid(conf) < 0) 3974c1d255d3SCy Schubert goto fail; 397585732ac8SCy Schubert return conf; 397685732ac8SCy Schubert fail: 397785732ac8SCy Schubert dpp_configurator_free(conf); 3978c1d255d3SCy Schubert return NULL; 397985732ac8SCy Schubert } 398085732ac8SCy Schubert 398185732ac8SCy Schubert 398285732ac8SCy Schubert int dpp_configurator_own_config(struct dpp_authentication *auth, 398385732ac8SCy Schubert const char *curve, int ap) 398485732ac8SCy Schubert { 398585732ac8SCy Schubert struct wpabuf *conf_obj; 398685732ac8SCy Schubert int ret = -1; 398785732ac8SCy Schubert 398885732ac8SCy Schubert if (!auth->conf) { 398985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No configurator specified"); 399085732ac8SCy Schubert return -1; 399185732ac8SCy Schubert } 399285732ac8SCy Schubert 399385732ac8SCy Schubert auth->curve = dpp_get_curve_name(curve); 399485732ac8SCy Schubert if (!auth->curve) { 3995c1d255d3SCy Schubert wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve); 399685732ac8SCy Schubert return -1; 399785732ac8SCy Schubert } 3998c1d255d3SCy Schubert 399985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 400085732ac8SCy Schubert "DPP: Building own configuration/connector with curve %s", 400185732ac8SCy Schubert auth->curve->name); 400285732ac8SCy Schubert 400385732ac8SCy Schubert auth->own_protocol_key = dpp_gen_keypair(auth->curve); 400485732ac8SCy Schubert if (!auth->own_protocol_key) 400585732ac8SCy Schubert return -1; 4006c1d255d3SCy Schubert dpp_copy_netaccesskey(auth, &auth->conf_obj[0]); 400785732ac8SCy Schubert auth->peer_protocol_key = auth->own_protocol_key; 4008c1d255d3SCy Schubert dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign); 400985732ac8SCy Schubert 4010c1d255d3SCy Schubert conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL); 4011c1d255d3SCy Schubert if (!conf_obj) { 4012c1d255d3SCy Schubert wpabuf_free(auth->conf_obj[0].c_sign_key); 4013c1d255d3SCy Schubert auth->conf_obj[0].c_sign_key = NULL; 401485732ac8SCy Schubert goto fail; 4015c1d255d3SCy Schubert } 401685732ac8SCy Schubert ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj), 401785732ac8SCy Schubert wpabuf_len(conf_obj)); 401885732ac8SCy Schubert fail: 401985732ac8SCy Schubert wpabuf_free(conf_obj); 402085732ac8SCy Schubert auth->peer_protocol_key = NULL; 402185732ac8SCy Schubert return ret; 402285732ac8SCy Schubert } 402385732ac8SCy Schubert 402485732ac8SCy Schubert 402585732ac8SCy Schubert static int dpp_compatible_netrole(const char *role1, const char *role2) 402685732ac8SCy Schubert { 402785732ac8SCy Schubert return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) || 402885732ac8SCy Schubert (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0); 402985732ac8SCy Schubert } 403085732ac8SCy Schubert 403185732ac8SCy Schubert 403285732ac8SCy Schubert static int dpp_connector_compatible_group(struct json_token *root, 403385732ac8SCy Schubert const char *group_id, 4034c1d255d3SCy Schubert const char *net_role, 4035c1d255d3SCy Schubert bool reconfig) 403685732ac8SCy Schubert { 403785732ac8SCy Schubert struct json_token *groups, *token; 403885732ac8SCy Schubert 403985732ac8SCy Schubert groups = json_get_member(root, "groups"); 404085732ac8SCy Schubert if (!groups || groups->type != JSON_ARRAY) 404185732ac8SCy Schubert return 0; 404285732ac8SCy Schubert 404385732ac8SCy Schubert for (token = groups->child; token; token = token->sibling) { 404485732ac8SCy Schubert struct json_token *id, *role; 404585732ac8SCy Schubert 404685732ac8SCy Schubert id = json_get_member(token, "groupId"); 404785732ac8SCy Schubert if (!id || id->type != JSON_STRING) 404885732ac8SCy Schubert continue; 404985732ac8SCy Schubert 405085732ac8SCy Schubert role = json_get_member(token, "netRole"); 405185732ac8SCy Schubert if (!role || role->type != JSON_STRING) 405285732ac8SCy Schubert continue; 405385732ac8SCy Schubert 405485732ac8SCy Schubert if (os_strcmp(id->string, "*") != 0 && 405585732ac8SCy Schubert os_strcmp(group_id, "*") != 0 && 405685732ac8SCy Schubert os_strcmp(id->string, group_id) != 0) 405785732ac8SCy Schubert continue; 405885732ac8SCy Schubert 4059c1d255d3SCy Schubert if (reconfig && os_strcmp(net_role, "configurator") == 0) 4060c1d255d3SCy Schubert return 1; 4061c1d255d3SCy Schubert if (!reconfig && dpp_compatible_netrole(role->string, net_role)) 406285732ac8SCy Schubert return 1; 406385732ac8SCy Schubert } 406485732ac8SCy Schubert 406585732ac8SCy Schubert return 0; 406685732ac8SCy Schubert } 406785732ac8SCy Schubert 406885732ac8SCy Schubert 4069c1d255d3SCy Schubert int dpp_connector_match_groups(struct json_token *own_root, 4070c1d255d3SCy Schubert struct json_token *peer_root, bool reconfig) 407185732ac8SCy Schubert { 407285732ac8SCy Schubert struct json_token *groups, *token; 407385732ac8SCy Schubert 407485732ac8SCy Schubert groups = json_get_member(peer_root, "groups"); 407585732ac8SCy Schubert if (!groups || groups->type != JSON_ARRAY) { 407685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No peer groups array found"); 407785732ac8SCy Schubert return 0; 407885732ac8SCy Schubert } 407985732ac8SCy Schubert 408085732ac8SCy Schubert for (token = groups->child; token; token = token->sibling) { 408185732ac8SCy Schubert struct json_token *id, *role; 408285732ac8SCy Schubert 408385732ac8SCy Schubert id = json_get_member(token, "groupId"); 408485732ac8SCy Schubert if (!id || id->type != JSON_STRING) { 408585732ac8SCy Schubert wpa_printf(MSG_DEBUG, 408685732ac8SCy Schubert "DPP: Missing peer groupId string"); 408785732ac8SCy Schubert continue; 408885732ac8SCy Schubert } 408985732ac8SCy Schubert 409085732ac8SCy Schubert role = json_get_member(token, "netRole"); 409185732ac8SCy Schubert if (!role || role->type != JSON_STRING) { 409285732ac8SCy Schubert wpa_printf(MSG_DEBUG, 409385732ac8SCy Schubert "DPP: Missing peer groups::netRole string"); 409485732ac8SCy Schubert continue; 409585732ac8SCy Schubert } 409685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 409785732ac8SCy Schubert "DPP: peer connector group: groupId='%s' netRole='%s'", 409885732ac8SCy Schubert id->string, role->string); 409985732ac8SCy Schubert if (dpp_connector_compatible_group(own_root, id->string, 4100c1d255d3SCy Schubert role->string, reconfig)) { 410185732ac8SCy Schubert wpa_printf(MSG_DEBUG, 410285732ac8SCy Schubert "DPP: Compatible group/netRole in own connector"); 410385732ac8SCy Schubert return 1; 410485732ac8SCy Schubert } 410585732ac8SCy Schubert } 410685732ac8SCy Schubert 410785732ac8SCy Schubert return 0; 410885732ac8SCy Schubert } 410985732ac8SCy Schubert 411085732ac8SCy Schubert 4111c1d255d3SCy Schubert struct json_token * dpp_parse_own_connector(const char *own_connector) 411285732ac8SCy Schubert { 4113c1d255d3SCy Schubert unsigned char *own_conn; 4114c1d255d3SCy Schubert size_t own_conn_len; 4115c1d255d3SCy Schubert const char *pos, *end; 4116c1d255d3SCy Schubert struct json_token *own_root; 411785732ac8SCy Schubert 4118c1d255d3SCy Schubert pos = os_strchr(own_connector, '.'); 4119c1d255d3SCy Schubert if (!pos) { 4120c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)"); 4121c1d255d3SCy Schubert return NULL; 4122c1d255d3SCy Schubert } 4123c1d255d3SCy Schubert pos++; 4124c1d255d3SCy Schubert end = os_strchr(pos, '.'); 4125c1d255d3SCy Schubert if (!end) { 4126c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)"); 4127c1d255d3SCy Schubert return NULL; 4128c1d255d3SCy Schubert } 4129c1d255d3SCy Schubert own_conn = base64_url_decode(pos, end - pos, &own_conn_len); 4130c1d255d3SCy Schubert if (!own_conn) { 4131c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 4132c1d255d3SCy Schubert "DPP: Failed to base64url decode own signedConnector JWS Payload"); 4133c1d255d3SCy Schubert return NULL; 413485732ac8SCy Schubert } 413585732ac8SCy Schubert 4136c1d255d3SCy Schubert own_root = json_parse((const char *) own_conn, own_conn_len); 4137c1d255d3SCy Schubert os_free(own_conn); 4138c1d255d3SCy Schubert if (!own_root) 4139c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector"); 414085732ac8SCy Schubert 4141c1d255d3SCy Schubert return own_root; 414285732ac8SCy Schubert } 414385732ac8SCy Schubert 414485732ac8SCy Schubert 414585732ac8SCy Schubert enum dpp_status_error 414685732ac8SCy Schubert dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, 414785732ac8SCy Schubert const u8 *net_access_key, size_t net_access_key_len, 414885732ac8SCy Schubert const u8 *csign_key, size_t csign_key_len, 414985732ac8SCy Schubert const u8 *peer_connector, size_t peer_connector_len, 4150*a90b9d01SCy Schubert os_time_t *expiry, u8 *peer_key_hash) 415185732ac8SCy Schubert { 415285732ac8SCy Schubert struct json_token *root = NULL, *netkey, *token; 415385732ac8SCy Schubert struct json_token *own_root = NULL; 415485732ac8SCy Schubert enum dpp_status_error ret = 255, res; 4155*a90b9d01SCy Schubert struct crypto_ec_key *own_key = NULL; 415685732ac8SCy Schubert struct wpabuf *own_key_pub = NULL; 415785732ac8SCy Schubert const struct dpp_curve_params *curve, *own_curve; 415885732ac8SCy Schubert struct dpp_signed_connector_info info; 415985732ac8SCy Schubert size_t Nx_len; 416085732ac8SCy Schubert u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; 416185732ac8SCy Schubert 416285732ac8SCy Schubert os_memset(intro, 0, sizeof(*intro)); 416385732ac8SCy Schubert os_memset(&info, 0, sizeof(info)); 416485732ac8SCy Schubert if (expiry) 416585732ac8SCy Schubert *expiry = 0; 416685732ac8SCy Schubert 416785732ac8SCy Schubert own_key = dpp_set_keypair(&own_curve, net_access_key, 416885732ac8SCy Schubert net_access_key_len); 416985732ac8SCy Schubert if (!own_key) { 417085732ac8SCy Schubert wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); 417185732ac8SCy Schubert goto fail; 417285732ac8SCy Schubert } 417385732ac8SCy Schubert 4174c1d255d3SCy Schubert own_root = dpp_parse_own_connector(own_connector); 4175c1d255d3SCy Schubert if (!own_root) 417685732ac8SCy Schubert goto fail; 417785732ac8SCy Schubert 4178c1d255d3SCy Schubert res = dpp_check_signed_connector(&info, csign_key, csign_key_len, 417985732ac8SCy Schubert peer_connector, peer_connector_len); 418085732ac8SCy Schubert if (res != DPP_STATUS_OK) { 418185732ac8SCy Schubert ret = res; 418285732ac8SCy Schubert goto fail; 418385732ac8SCy Schubert } 418485732ac8SCy Schubert 418585732ac8SCy Schubert root = json_parse((const char *) info.payload, info.payload_len); 418685732ac8SCy Schubert if (!root) { 418785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); 418885732ac8SCy Schubert ret = DPP_STATUS_INVALID_CONNECTOR; 418985732ac8SCy Schubert goto fail; 419085732ac8SCy Schubert } 419185732ac8SCy Schubert 4192c1d255d3SCy Schubert if (!dpp_connector_match_groups(own_root, root, false)) { 419385732ac8SCy Schubert wpa_printf(MSG_DEBUG, 419485732ac8SCy Schubert "DPP: Peer connector does not include compatible group netrole with own connector"); 419585732ac8SCy Schubert ret = DPP_STATUS_NO_MATCH; 419685732ac8SCy Schubert goto fail; 419785732ac8SCy Schubert } 419885732ac8SCy Schubert 419985732ac8SCy Schubert token = json_get_member(root, "expiry"); 420085732ac8SCy Schubert if (!token || token->type != JSON_STRING) { 420185732ac8SCy Schubert wpa_printf(MSG_DEBUG, 420285732ac8SCy Schubert "DPP: No expiry string found - connector does not expire"); 420385732ac8SCy Schubert } else { 420485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); 420585732ac8SCy Schubert if (dpp_key_expired(token->string, expiry)) { 420685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 420785732ac8SCy Schubert "DPP: Connector (netAccessKey) has expired"); 420885732ac8SCy Schubert ret = DPP_STATUS_INVALID_CONNECTOR; 420985732ac8SCy Schubert goto fail; 421085732ac8SCy Schubert } 421185732ac8SCy Schubert } 421285732ac8SCy Schubert 421332a95656SCy Schubert #ifdef CONFIG_DPP3 421432a95656SCy Schubert token = json_get_member(root, "version"); 421532a95656SCy Schubert if (token && token->type == JSON_NUMBER) { 421632a95656SCy Schubert wpa_printf(MSG_DEBUG, "DPP: version = %d", token->number); 421732a95656SCy Schubert intro->peer_version = token->number; 421832a95656SCy Schubert } 421932a95656SCy Schubert #endif /* CONFIG_DPP3 */ 422032a95656SCy Schubert 422185732ac8SCy Schubert netkey = json_get_member(root, "netAccessKey"); 422285732ac8SCy Schubert if (!netkey || netkey->type != JSON_OBJECT) { 422385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); 422485732ac8SCy Schubert ret = DPP_STATUS_INVALID_CONNECTOR; 422585732ac8SCy Schubert goto fail; 422685732ac8SCy Schubert } 422785732ac8SCy Schubert 4228*a90b9d01SCy Schubert intro->peer_key = dpp_parse_jwk(netkey, &curve); 4229*a90b9d01SCy Schubert if (!intro->peer_key) { 423085732ac8SCy Schubert ret = DPP_STATUS_INVALID_CONNECTOR; 423185732ac8SCy Schubert goto fail; 423285732ac8SCy Schubert } 4233*a90b9d01SCy Schubert dpp_debug_print_key("DPP: Received netAccessKey", intro->peer_key); 423485732ac8SCy Schubert 423585732ac8SCy Schubert if (own_curve != curve) { 423685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 423785732ac8SCy Schubert "DPP: Mismatching netAccessKey curves (%s != %s)", 423885732ac8SCy Schubert own_curve->name, curve->name); 423985732ac8SCy Schubert ret = DPP_STATUS_INVALID_CONNECTOR; 424085732ac8SCy Schubert goto fail; 424185732ac8SCy Schubert } 424285732ac8SCy Schubert 424385732ac8SCy Schubert /* ECDH: N = nk * PK */ 4244*a90b9d01SCy Schubert if (dpp_ecdh(own_key, intro->peer_key, Nx, &Nx_len) < 0) 424585732ac8SCy Schubert goto fail; 424685732ac8SCy Schubert 424785732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", 424885732ac8SCy Schubert Nx, Nx_len); 424985732ac8SCy Schubert 425085732ac8SCy Schubert /* PMK = HKDF(<>, "DPP PMK", N.x) */ 425185732ac8SCy Schubert if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) { 425285732ac8SCy Schubert wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK"); 425385732ac8SCy Schubert goto fail; 425485732ac8SCy Schubert } 425585732ac8SCy Schubert intro->pmk_len = curve->hash_len; 425685732ac8SCy Schubert 425785732ac8SCy Schubert /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ 4258*a90b9d01SCy Schubert if (dpp_derive_pmkid(curve, own_key, intro->peer_key, intro->pmkid) < 4259*a90b9d01SCy Schubert 0) { 426085732ac8SCy Schubert wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID"); 426185732ac8SCy Schubert goto fail; 426285732ac8SCy Schubert } 426385732ac8SCy Schubert 4264*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 4265*a90b9d01SCy Schubert if (dpp_hpke_suite(curve->ike_group, &intro->kem_id, &intro->kdf_id, 4266*a90b9d01SCy Schubert &intro->aead_id) < 0) { 4267*a90b9d01SCy Schubert wpa_printf(MSG_ERROR, "DPP: Unsupported group %d", 4268*a90b9d01SCy Schubert curve->ike_group); 4269*a90b9d01SCy Schubert goto fail; 4270*a90b9d01SCy Schubert } 4271*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 4272*a90b9d01SCy Schubert 4273*a90b9d01SCy Schubert if (peer_key_hash) 4274*a90b9d01SCy Schubert dpp_get_pubkey_hash(intro->peer_key, peer_key_hash); 4275*a90b9d01SCy Schubert 427685732ac8SCy Schubert ret = DPP_STATUS_OK; 427785732ac8SCy Schubert fail: 427885732ac8SCy Schubert if (ret != DPP_STATUS_OK) 4279*a90b9d01SCy Schubert dpp_peer_intro_deinit(intro); 428085732ac8SCy Schubert os_memset(Nx, 0, sizeof(Nx)); 428185732ac8SCy Schubert os_free(info.payload); 42824b72b91aSCy Schubert crypto_ec_key_deinit(own_key); 428385732ac8SCy Schubert wpabuf_free(own_key_pub); 428485732ac8SCy Schubert json_free(root); 428585732ac8SCy Schubert json_free(own_root); 428685732ac8SCy Schubert return ret; 428785732ac8SCy Schubert } 428885732ac8SCy Schubert 428985732ac8SCy Schubert 4290*a90b9d01SCy Schubert void dpp_peer_intro_deinit(struct dpp_introduction *intro) 4291*a90b9d01SCy Schubert { 4292*a90b9d01SCy Schubert if (!intro) 4293*a90b9d01SCy Schubert return; 4294*a90b9d01SCy Schubert 4295*a90b9d01SCy Schubert crypto_ec_key_deinit(intro->peer_key); 4296*a90b9d01SCy Schubert os_memset(intro, 0, sizeof(*intro)); 4297*a90b9d01SCy Schubert } 4298*a90b9d01SCy Schubert 4299*a90b9d01SCy Schubert 430032a95656SCy Schubert #ifdef CONFIG_DPP3 430132a95656SCy Schubert int dpp_get_connector_version(const char *connector) 430232a95656SCy Schubert { 430332a95656SCy Schubert struct json_token *root, *token; 430432a95656SCy Schubert int ver = -1; 430532a95656SCy Schubert 430632a95656SCy Schubert root = dpp_parse_own_connector(connector); 430732a95656SCy Schubert if (!root) 430832a95656SCy Schubert return -1; 430932a95656SCy Schubert 431032a95656SCy Schubert token = json_get_member(root, "version"); 431132a95656SCy Schubert if (token && token->type == JSON_NUMBER) 431232a95656SCy Schubert ver = token->number; 431332a95656SCy Schubert 431432a95656SCy Schubert json_free(root); 431532a95656SCy Schubert return ver; 431632a95656SCy Schubert } 431732a95656SCy Schubert #endif /* CONFIG_DPP3 */ 431832a95656SCy Schubert 431932a95656SCy Schubert 4320c1d255d3SCy Schubert unsigned int dpp_next_id(struct dpp_global *dpp) 43214bc52338SCy Schubert { 43224bc52338SCy Schubert struct dpp_bootstrap_info *bi; 43234bc52338SCy Schubert unsigned int max_id = 0; 43244bc52338SCy Schubert 43254bc52338SCy Schubert dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 43264bc52338SCy Schubert if (bi->id > max_id) 43274bc52338SCy Schubert max_id = bi->id; 43284bc52338SCy Schubert } 43294bc52338SCy Schubert return max_id + 1; 43304bc52338SCy Schubert } 43314bc52338SCy Schubert 43324bc52338SCy Schubert 43334bc52338SCy Schubert static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id) 43344bc52338SCy Schubert { 43354bc52338SCy Schubert struct dpp_bootstrap_info *bi, *tmp; 43364bc52338SCy Schubert int found = 0; 43374bc52338SCy Schubert 43384bc52338SCy Schubert if (!dpp) 43394bc52338SCy Schubert return -1; 43404bc52338SCy Schubert 43414bc52338SCy Schubert dl_list_for_each_safe(bi, tmp, &dpp->bootstrap, 43424bc52338SCy Schubert struct dpp_bootstrap_info, list) { 43434bc52338SCy Schubert if (id && bi->id != id) 43444bc52338SCy Schubert continue; 43454bc52338SCy Schubert found = 1; 4346c1d255d3SCy Schubert #ifdef CONFIG_DPP2 4347c1d255d3SCy Schubert if (dpp->remove_bi) 4348c1d255d3SCy Schubert dpp->remove_bi(dpp->cb_ctx, bi); 4349c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 43504bc52338SCy Schubert dl_list_del(&bi->list); 43514bc52338SCy Schubert dpp_bootstrap_info_free(bi); 43524bc52338SCy Schubert } 43534bc52338SCy Schubert 43544bc52338SCy Schubert if (id == 0) 43554bc52338SCy Schubert return 0; /* flush succeeds regardless of entries found */ 43564bc52338SCy Schubert return found ? 0 : -1; 43574bc52338SCy Schubert } 43584bc52338SCy Schubert 43594bc52338SCy Schubert 43604bc52338SCy Schubert struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, 43614bc52338SCy Schubert const char *uri) 43624bc52338SCy Schubert { 43634bc52338SCy Schubert struct dpp_bootstrap_info *bi; 43644bc52338SCy Schubert 43654bc52338SCy Schubert if (!dpp) 43664bc52338SCy Schubert return NULL; 43674bc52338SCy Schubert 4368c1d255d3SCy Schubert bi = dpp_parse_uri(uri); 43694bc52338SCy Schubert if (!bi) 43704bc52338SCy Schubert return NULL; 43714bc52338SCy Schubert 4372c1d255d3SCy Schubert bi->type = DPP_BOOTSTRAP_QR_CODE; 4373c1d255d3SCy Schubert bi->id = dpp_next_id(dpp); 4374c1d255d3SCy Schubert dl_list_add(&dpp->bootstrap, &bi->list); 4375c1d255d3SCy Schubert return bi; 4376c1d255d3SCy Schubert } 4377c1d255d3SCy Schubert 4378c1d255d3SCy Schubert 4379c1d255d3SCy Schubert struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp, 4380c1d255d3SCy Schubert const char *uri) 4381c1d255d3SCy Schubert { 4382c1d255d3SCy Schubert struct dpp_bootstrap_info *bi; 4383c1d255d3SCy Schubert 4384c1d255d3SCy Schubert if (!dpp) 4385c1d255d3SCy Schubert return NULL; 4386c1d255d3SCy Schubert 4387c1d255d3SCy Schubert bi = dpp_parse_uri(uri); 4388c1d255d3SCy Schubert if (!bi) 4389c1d255d3SCy Schubert return NULL; 4390c1d255d3SCy Schubert 4391c1d255d3SCy Schubert bi->type = DPP_BOOTSTRAP_NFC_URI; 43924bc52338SCy Schubert bi->id = dpp_next_id(dpp); 43934bc52338SCy Schubert dl_list_add(&dpp->bootstrap, &bi->list); 43944bc52338SCy Schubert return bi; 43954bc52338SCy Schubert } 43964bc52338SCy Schubert 43974bc52338SCy Schubert 4398*a90b9d01SCy Schubert static int dpp_parse_supported_curves_list(struct dpp_bootstrap_info *bi, 4399*a90b9d01SCy Schubert char *txt) 4400*a90b9d01SCy Schubert { 4401*a90b9d01SCy Schubert char *token, *context = NULL; 4402*a90b9d01SCy Schubert u8 curves = 0; 4403*a90b9d01SCy Schubert 4404*a90b9d01SCy Schubert if (!txt) 4405*a90b9d01SCy Schubert return 0; 4406*a90b9d01SCy Schubert 4407*a90b9d01SCy Schubert while ((token = str_token(txt, ":", &context))) { 4408*a90b9d01SCy Schubert if (os_strcmp(token, "P-256") == 0) { 4409*a90b9d01SCy Schubert curves |= BIT(DPP_BOOTSTRAP_CURVE_P_256); 4410*a90b9d01SCy Schubert } else if (os_strcmp(token, "P-384") == 0) { 4411*a90b9d01SCy Schubert curves |= BIT(DPP_BOOTSTRAP_CURVE_P_384); 4412*a90b9d01SCy Schubert } else if (os_strcmp(token, "P-521") == 0) { 4413*a90b9d01SCy Schubert curves |= BIT(DPP_BOOTSTRAP_CURVE_P_521); 4414*a90b9d01SCy Schubert } else if (os_strcmp(token, "BP-256") == 0) { 4415*a90b9d01SCy Schubert curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_256); 4416*a90b9d01SCy Schubert } else if (os_strcmp(token, "BP-384") == 0) { 4417*a90b9d01SCy Schubert curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_384); 4418*a90b9d01SCy Schubert } else if (os_strcmp(token, "BP-512") == 0) { 4419*a90b9d01SCy Schubert curves |= BIT(DPP_BOOTSTRAP_CURVE_BP_512); 4420*a90b9d01SCy Schubert } else { 4421*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Unsupported curve '%s'", 4422*a90b9d01SCy Schubert token); 4423*a90b9d01SCy Schubert return -1; 4424*a90b9d01SCy Schubert } 4425*a90b9d01SCy Schubert } 4426*a90b9d01SCy Schubert bi->supported_curves = curves; 4427*a90b9d01SCy Schubert 4428*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: URI supported curves: 0x%x", 4429*a90b9d01SCy Schubert bi->supported_curves); 4430*a90b9d01SCy Schubert 4431*a90b9d01SCy Schubert return 0; 4432*a90b9d01SCy Schubert } 4433*a90b9d01SCy Schubert 4434*a90b9d01SCy Schubert 44354bc52338SCy Schubert int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd) 44364bc52338SCy Schubert { 4437c1d255d3SCy Schubert char *mac = NULL, *info = NULL, *curve = NULL; 4438*a90b9d01SCy Schubert char *key = NULL, *supported_curves = NULL, *host = NULL; 44394bc52338SCy Schubert u8 *privkey = NULL; 44404bc52338SCy Schubert size_t privkey_len = 0; 44414bc52338SCy Schubert int ret = -1; 44424bc52338SCy Schubert struct dpp_bootstrap_info *bi; 44434bc52338SCy Schubert 44444bc52338SCy Schubert if (!dpp) 44454bc52338SCy Schubert return -1; 44464bc52338SCy Schubert 44474bc52338SCy Schubert bi = os_zalloc(sizeof(*bi)); 44484bc52338SCy Schubert if (!bi) 44494bc52338SCy Schubert goto fail; 44504bc52338SCy Schubert 44514bc52338SCy Schubert if (os_strstr(cmd, "type=qrcode")) 44524bc52338SCy Schubert bi->type = DPP_BOOTSTRAP_QR_CODE; 44534bc52338SCy Schubert else if (os_strstr(cmd, "type=pkex")) 44544bc52338SCy Schubert bi->type = DPP_BOOTSTRAP_PKEX; 4455c1d255d3SCy Schubert else if (os_strstr(cmd, "type=nfc-uri")) 4456c1d255d3SCy Schubert bi->type = DPP_BOOTSTRAP_NFC_URI; 44574bc52338SCy Schubert else 44584bc52338SCy Schubert goto fail; 44594bc52338SCy Schubert 4460c1d255d3SCy Schubert bi->chan = get_param(cmd, " chan="); 44614bc52338SCy Schubert mac = get_param(cmd, " mac="); 44624bc52338SCy Schubert info = get_param(cmd, " info="); 44634bc52338SCy Schubert curve = get_param(cmd, " curve="); 44644bc52338SCy Schubert key = get_param(cmd, " key="); 4465*a90b9d01SCy Schubert supported_curves = get_param(cmd, " supported_curves="); 4466*a90b9d01SCy Schubert host = get_param(cmd, " host="); 44674bc52338SCy Schubert 44684bc52338SCy Schubert if (key) { 44694bc52338SCy Schubert privkey_len = os_strlen(key) / 2; 44704bc52338SCy Schubert privkey = os_malloc(privkey_len); 44714bc52338SCy Schubert if (!privkey || 44724bc52338SCy Schubert hexstr2bin(key, privkey, privkey_len) < 0) 44734bc52338SCy Schubert goto fail; 44744bc52338SCy Schubert } 44754bc52338SCy Schubert 4476c1d255d3SCy Schubert if (dpp_keygen(bi, curve, privkey, privkey_len) < 0 || 4477c1d255d3SCy Schubert dpp_parse_uri_chan_list(bi, bi->chan) < 0 || 4478c1d255d3SCy Schubert dpp_parse_uri_mac(bi, mac) < 0 || 4479c1d255d3SCy Schubert dpp_parse_uri_info(bi, info) < 0 || 4480*a90b9d01SCy Schubert dpp_parse_supported_curves_list(bi, supported_curves) < 0 || 4481*a90b9d01SCy Schubert dpp_parse_uri_host(bi, host) < 0 || 4482c1d255d3SCy Schubert dpp_gen_uri(bi) < 0) 44834bc52338SCy Schubert goto fail; 44844bc52338SCy Schubert 44854bc52338SCy Schubert bi->id = dpp_next_id(dpp); 44864bc52338SCy Schubert dl_list_add(&dpp->bootstrap, &bi->list); 44874bc52338SCy Schubert ret = bi->id; 44884bc52338SCy Schubert bi = NULL; 44894bc52338SCy Schubert fail: 44904bc52338SCy Schubert os_free(curve); 44914bc52338SCy Schubert os_free(mac); 44924bc52338SCy Schubert os_free(info); 44934bc52338SCy Schubert str_clear_free(key); 4494*a90b9d01SCy Schubert os_free(supported_curves); 4495*a90b9d01SCy Schubert os_free(host); 44964bc52338SCy Schubert bin_clear_free(privkey, privkey_len); 44974bc52338SCy Schubert dpp_bootstrap_info_free(bi); 44984bc52338SCy Schubert return ret; 44994bc52338SCy Schubert } 45004bc52338SCy Schubert 45014bc52338SCy Schubert 45024bc52338SCy Schubert struct dpp_bootstrap_info * 45034bc52338SCy Schubert dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id) 45044bc52338SCy Schubert { 45054bc52338SCy Schubert struct dpp_bootstrap_info *bi; 45064bc52338SCy Schubert 45074bc52338SCy Schubert if (!dpp) 45084bc52338SCy Schubert return NULL; 45094bc52338SCy Schubert 45104bc52338SCy Schubert dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 45114bc52338SCy Schubert if (bi->id == id) 45124bc52338SCy Schubert return bi; 45134bc52338SCy Schubert } 45144bc52338SCy Schubert return NULL; 45154bc52338SCy Schubert } 45164bc52338SCy Schubert 45174bc52338SCy Schubert 45184bc52338SCy Schubert int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id) 45194bc52338SCy Schubert { 45204bc52338SCy Schubert unsigned int id_val; 45214bc52338SCy Schubert 45224bc52338SCy Schubert if (os_strcmp(id, "*") == 0) { 45234bc52338SCy Schubert id_val = 0; 45244bc52338SCy Schubert } else { 45254bc52338SCy Schubert id_val = atoi(id); 45264bc52338SCy Schubert if (id_val == 0) 45274bc52338SCy Schubert return -1; 45284bc52338SCy Schubert } 45294bc52338SCy Schubert 45304bc52338SCy Schubert return dpp_bootstrap_del(dpp, id_val); 45314bc52338SCy Schubert } 45324bc52338SCy Schubert 45334bc52338SCy Schubert 45344bc52338SCy Schubert const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id) 45354bc52338SCy Schubert { 45364bc52338SCy Schubert struct dpp_bootstrap_info *bi; 45374bc52338SCy Schubert 45384bc52338SCy Schubert bi = dpp_bootstrap_get_id(dpp, id); 45394bc52338SCy Schubert if (!bi) 45404bc52338SCy Schubert return NULL; 45414bc52338SCy Schubert return bi->uri; 45424bc52338SCy Schubert } 45434bc52338SCy Schubert 45444bc52338SCy Schubert 45454bc52338SCy Schubert int dpp_bootstrap_info(struct dpp_global *dpp, int id, 45464bc52338SCy Schubert char *reply, int reply_size) 45474bc52338SCy Schubert { 45484bc52338SCy Schubert struct dpp_bootstrap_info *bi; 4549206b73d0SCy Schubert char pkhash[2 * SHA256_MAC_LEN + 1]; 4550*a90b9d01SCy Schubert char supp_curves[100]; 4551*a90b9d01SCy Schubert char host[100]; 4552*a90b9d01SCy Schubert int ret; 45534bc52338SCy Schubert 45544bc52338SCy Schubert bi = dpp_bootstrap_get_id(dpp, id); 45554bc52338SCy Schubert if (!bi) 45564bc52338SCy Schubert return -1; 4557206b73d0SCy Schubert wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash, 4558206b73d0SCy Schubert SHA256_MAC_LEN); 4559*a90b9d01SCy Schubert 4560*a90b9d01SCy Schubert supp_curves[0] = '\0'; 4561*a90b9d01SCy Schubert if (bi->supported_curves) { 4562*a90b9d01SCy Schubert size_t i; 4563*a90b9d01SCy Schubert char *pos = supp_curves; 4564*a90b9d01SCy Schubert char *end = &supp_curves[sizeof(supp_curves)]; 4565*a90b9d01SCy Schubert const char *curve[6] = { "P-256", "P-384", "P-521", 4566*a90b9d01SCy Schubert "BP-256", "BP-384", "BP-512" }; 4567*a90b9d01SCy Schubert 4568*a90b9d01SCy Schubert ret = os_snprintf(pos, end - pos, "supp_curves="); 4569*a90b9d01SCy Schubert if (os_snprintf_error(end - pos, ret)) 4570*a90b9d01SCy Schubert return -1; 4571*a90b9d01SCy Schubert pos += ret; 4572*a90b9d01SCy Schubert 4573*a90b9d01SCy Schubert for (i = 0; i < ARRAY_SIZE(curve); i++) { 4574*a90b9d01SCy Schubert if (!(bi->supported_curves & BIT(i))) 4575*a90b9d01SCy Schubert continue; 4576*a90b9d01SCy Schubert ret = os_snprintf(pos, end - pos, "%s:", curve[i]); 4577*a90b9d01SCy Schubert if (os_snprintf_error(end - pos, ret)) 4578*a90b9d01SCy Schubert return -1; 4579*a90b9d01SCy Schubert pos += ret; 4580*a90b9d01SCy Schubert } 4581*a90b9d01SCy Schubert 4582*a90b9d01SCy Schubert if (pos[-1] == ':') 4583*a90b9d01SCy Schubert pos[-1] = '\n'; 4584*a90b9d01SCy Schubert else 4585*a90b9d01SCy Schubert supp_curves[0] = '\0'; 4586*a90b9d01SCy Schubert } 4587*a90b9d01SCy Schubert 4588*a90b9d01SCy Schubert host[0] = '\0'; 4589*a90b9d01SCy Schubert if (bi->host) { 4590*a90b9d01SCy Schubert char buf[100]; 4591*a90b9d01SCy Schubert 4592*a90b9d01SCy Schubert ret = os_snprintf(host, sizeof(host), "host=%s %u\n", 4593*a90b9d01SCy Schubert hostapd_ip_txt(bi->host, buf, sizeof(buf)), 4594*a90b9d01SCy Schubert bi->port); 4595*a90b9d01SCy Schubert if (os_snprintf_error(sizeof(host), ret)) 4596*a90b9d01SCy Schubert return -1; 4597*a90b9d01SCy Schubert } 4598*a90b9d01SCy Schubert 45994bc52338SCy Schubert return os_snprintf(reply, reply_size, "type=%s\n" 46004bc52338SCy Schubert "mac_addr=" MACSTR "\n" 46014bc52338SCy Schubert "info=%s\n" 46024bc52338SCy Schubert "num_freq=%u\n" 4603c1d255d3SCy Schubert "use_freq=%u\n" 4604206b73d0SCy Schubert "curve=%s\n" 4605c1d255d3SCy Schubert "pkhash=%s\n" 4606*a90b9d01SCy Schubert "version=%d\n%s%s", 46074bc52338SCy Schubert dpp_bootstrap_type_txt(bi->type), 46084bc52338SCy Schubert MAC2STR(bi->mac_addr), 46094bc52338SCy Schubert bi->info ? bi->info : "", 46104bc52338SCy Schubert bi->num_freq, 4611c1d255d3SCy Schubert bi->num_freq == 1 ? bi->freq[0] : 0, 4612206b73d0SCy Schubert bi->curve->name, 4613c1d255d3SCy Schubert pkhash, 4614*a90b9d01SCy Schubert bi->version, 4615*a90b9d01SCy Schubert supp_curves, 4616*a90b9d01SCy Schubert host); 4617c1d255d3SCy Schubert } 4618c1d255d3SCy Schubert 4619c1d255d3SCy Schubert 4620c1d255d3SCy Schubert int dpp_bootstrap_set(struct dpp_global *dpp, int id, const char *params) 4621c1d255d3SCy Schubert { 4622c1d255d3SCy Schubert struct dpp_bootstrap_info *bi; 4623c1d255d3SCy Schubert 4624c1d255d3SCy Schubert bi = dpp_bootstrap_get_id(dpp, id); 4625c1d255d3SCy Schubert if (!bi) 4626c1d255d3SCy Schubert return -1; 4627c1d255d3SCy Schubert 4628c1d255d3SCy Schubert str_clear_free(bi->configurator_params); 4629c1d255d3SCy Schubert 4630c1d255d3SCy Schubert if (params) { 4631c1d255d3SCy Schubert bi->configurator_params = os_strdup(params); 4632c1d255d3SCy Schubert return bi->configurator_params ? 0 : -1; 4633c1d255d3SCy Schubert } 4634c1d255d3SCy Schubert 4635c1d255d3SCy Schubert bi->configurator_params = NULL; 4636c1d255d3SCy Schubert return 0; 46374bc52338SCy Schubert } 46384bc52338SCy Schubert 46394bc52338SCy Schubert 46404bc52338SCy Schubert void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, 46414bc52338SCy Schubert const u8 *r_bootstrap, 46424bc52338SCy Schubert struct dpp_bootstrap_info **own_bi, 46434bc52338SCy Schubert struct dpp_bootstrap_info **peer_bi) 46444bc52338SCy Schubert { 46454bc52338SCy Schubert struct dpp_bootstrap_info *bi; 46464bc52338SCy Schubert 46474bc52338SCy Schubert *own_bi = NULL; 46484bc52338SCy Schubert *peer_bi = NULL; 46494bc52338SCy Schubert if (!dpp) 46504bc52338SCy Schubert return; 46514bc52338SCy Schubert 46524bc52338SCy Schubert dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 46534bc52338SCy Schubert if (!*own_bi && bi->own && 46544bc52338SCy Schubert os_memcmp(bi->pubkey_hash, r_bootstrap, 46554bc52338SCy Schubert SHA256_MAC_LEN) == 0) { 46564bc52338SCy Schubert wpa_printf(MSG_DEBUG, 46574bc52338SCy Schubert "DPP: Found matching own bootstrapping information"); 46584bc52338SCy Schubert *own_bi = bi; 46594bc52338SCy Schubert } 46604bc52338SCy Schubert 46614bc52338SCy Schubert if (!*peer_bi && !bi->own && 46624bc52338SCy Schubert os_memcmp(bi->pubkey_hash, i_bootstrap, 46634bc52338SCy Schubert SHA256_MAC_LEN) == 0) { 46644bc52338SCy Schubert wpa_printf(MSG_DEBUG, 46654bc52338SCy Schubert "DPP: Found matching peer bootstrapping information"); 46664bc52338SCy Schubert *peer_bi = bi; 46674bc52338SCy Schubert } 46684bc52338SCy Schubert 46694bc52338SCy Schubert if (*own_bi && *peer_bi) 46704bc52338SCy Schubert break; 46714bc52338SCy Schubert } 4672c1d255d3SCy Schubert } 46734bc52338SCy Schubert 4674c1d255d3SCy Schubert 4675c1d255d3SCy Schubert #ifdef CONFIG_DPP2 4676c1d255d3SCy Schubert struct dpp_bootstrap_info * dpp_bootstrap_find_chirp(struct dpp_global *dpp, 4677c1d255d3SCy Schubert const u8 *hash) 4678c1d255d3SCy Schubert { 4679c1d255d3SCy Schubert struct dpp_bootstrap_info *bi; 4680c1d255d3SCy Schubert 4681c1d255d3SCy Schubert if (!dpp) 4682c1d255d3SCy Schubert return NULL; 4683c1d255d3SCy Schubert 4684c1d255d3SCy Schubert dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 4685c1d255d3SCy Schubert if (!bi->own && os_memcmp(bi->pubkey_hash_chirp, hash, 4686c1d255d3SCy Schubert SHA256_MAC_LEN) == 0) 4687c1d255d3SCy Schubert return bi; 4688c1d255d3SCy Schubert } 4689c1d255d3SCy Schubert 4690c1d255d3SCy Schubert return NULL; 4691c1d255d3SCy Schubert } 4692c1d255d3SCy Schubert #endif /* CONFIG_DPP2 */ 4693c1d255d3SCy Schubert 4694c1d255d3SCy Schubert 4695c1d255d3SCy Schubert static int dpp_nfc_update_bi_channel(struct dpp_bootstrap_info *own_bi, 4696c1d255d3SCy Schubert struct dpp_bootstrap_info *peer_bi) 4697c1d255d3SCy Schubert { 4698c1d255d3SCy Schubert unsigned int i, freq = 0; 4699c1d255d3SCy Schubert enum hostapd_hw_mode mode; 4700c1d255d3SCy Schubert u8 op_class, channel; 4701c1d255d3SCy Schubert char chan[20]; 4702c1d255d3SCy Schubert 4703c1d255d3SCy Schubert if (peer_bi->num_freq == 0 && !peer_bi->channels_listed) 4704c1d255d3SCy Schubert return 0; /* no channel preference/constraint */ 4705c1d255d3SCy Schubert 4706c1d255d3SCy Schubert for (i = 0; i < peer_bi->num_freq; i++) { 4707c1d255d3SCy Schubert if ((own_bi->num_freq == 0 && !own_bi->channels_listed) || 4708c1d255d3SCy Schubert freq_included(own_bi->freq, own_bi->num_freq, 4709c1d255d3SCy Schubert peer_bi->freq[i])) { 4710c1d255d3SCy Schubert freq = peer_bi->freq[i]; 4711c1d255d3SCy Schubert break; 4712c1d255d3SCy Schubert } 4713c1d255d3SCy Schubert } 4714c1d255d3SCy Schubert if (!freq) { 4715c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: No common channel found"); 4716c1d255d3SCy Schubert return -1; 4717c1d255d3SCy Schubert } 4718c1d255d3SCy Schubert 4719c1d255d3SCy Schubert mode = ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, &channel); 4720c1d255d3SCy Schubert if (mode == NUM_HOSTAPD_MODES) { 4721c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 4722c1d255d3SCy Schubert "DPP: Could not determine operating class or channel number for %u MHz", 4723c1d255d3SCy Schubert freq); 4724c1d255d3SCy Schubert } 4725c1d255d3SCy Schubert 4726c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 4727c1d255d3SCy Schubert "DPP: Selected %u MHz (op_class %u channel %u) as the negotiation channel based on information from NFC negotiated handover", 4728c1d255d3SCy Schubert freq, op_class, channel); 4729c1d255d3SCy Schubert os_snprintf(chan, sizeof(chan), "%u/%u", op_class, channel); 4730c1d255d3SCy Schubert os_free(own_bi->chan); 4731c1d255d3SCy Schubert own_bi->chan = os_strdup(chan); 4732c1d255d3SCy Schubert own_bi->freq[0] = freq; 4733c1d255d3SCy Schubert own_bi->num_freq = 1; 4734c1d255d3SCy Schubert os_free(peer_bi->chan); 4735c1d255d3SCy Schubert peer_bi->chan = os_strdup(chan); 4736c1d255d3SCy Schubert peer_bi->freq[0] = freq; 4737c1d255d3SCy Schubert peer_bi->num_freq = 1; 4738c1d255d3SCy Schubert 4739c1d255d3SCy Schubert return dpp_gen_uri(own_bi); 4740c1d255d3SCy Schubert } 4741c1d255d3SCy Schubert 4742c1d255d3SCy Schubert 4743c1d255d3SCy Schubert static int dpp_nfc_update_bi_key(struct dpp_bootstrap_info *own_bi, 4744c1d255d3SCy Schubert struct dpp_bootstrap_info *peer_bi) 4745c1d255d3SCy Schubert { 4746c1d255d3SCy Schubert if (peer_bi->curve == own_bi->curve) 4747c1d255d3SCy Schubert return 0; 4748c1d255d3SCy Schubert 4749c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 4750c1d255d3SCy Schubert "DPP: Update own bootstrapping key to match peer curve from NFC handover"); 4751c1d255d3SCy Schubert 47524b72b91aSCy Schubert crypto_ec_key_deinit(own_bi->pubkey); 4753c1d255d3SCy Schubert own_bi->pubkey = NULL; 4754c1d255d3SCy Schubert 4755c1d255d3SCy Schubert if (dpp_keygen(own_bi, peer_bi->curve->name, NULL, 0) < 0 || 4756c1d255d3SCy Schubert dpp_gen_uri(own_bi) < 0) 4757c1d255d3SCy Schubert goto fail; 4758c1d255d3SCy Schubert 4759c1d255d3SCy Schubert return 0; 4760c1d255d3SCy Schubert fail: 4761c1d255d3SCy Schubert dl_list_del(&own_bi->list); 4762c1d255d3SCy Schubert dpp_bootstrap_info_free(own_bi); 4763c1d255d3SCy Schubert return -1; 4764c1d255d3SCy Schubert } 4765c1d255d3SCy Schubert 4766c1d255d3SCy Schubert 4767c1d255d3SCy Schubert int dpp_nfc_update_bi(struct dpp_bootstrap_info *own_bi, 4768c1d255d3SCy Schubert struct dpp_bootstrap_info *peer_bi) 4769c1d255d3SCy Schubert { 4770c1d255d3SCy Schubert if (dpp_nfc_update_bi_channel(own_bi, peer_bi) < 0 || 4771c1d255d3SCy Schubert dpp_nfc_update_bi_key(own_bi, peer_bi) < 0) 4772c1d255d3SCy Schubert return -1; 4773c1d255d3SCy Schubert return 0; 47744bc52338SCy Schubert } 47754bc52338SCy Schubert 47764bc52338SCy Schubert 47774bc52338SCy Schubert static unsigned int dpp_next_configurator_id(struct dpp_global *dpp) 47784bc52338SCy Schubert { 47794bc52338SCy Schubert struct dpp_configurator *conf; 47804bc52338SCy Schubert unsigned int max_id = 0; 47814bc52338SCy Schubert 47824bc52338SCy Schubert dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator, 47834bc52338SCy Schubert list) { 47844bc52338SCy Schubert if (conf->id > max_id) 47854bc52338SCy Schubert max_id = conf->id; 47864bc52338SCy Schubert } 47874bc52338SCy Schubert return max_id + 1; 47884bc52338SCy Schubert } 47894bc52338SCy Schubert 47904bc52338SCy Schubert 47914bc52338SCy Schubert int dpp_configurator_add(struct dpp_global *dpp, const char *cmd) 47924bc52338SCy Schubert { 4793*a90b9d01SCy Schubert char *curve; 4794c1d255d3SCy Schubert char *key = NULL, *ppkey = NULL; 4795c1d255d3SCy Schubert u8 *privkey = NULL, *pp_key = NULL; 4796c1d255d3SCy Schubert size_t privkey_len = 0, pp_key_len = 0; 47974bc52338SCy Schubert int ret = -1; 47984bc52338SCy Schubert struct dpp_configurator *conf = NULL; 4799*a90b9d01SCy Schubert const struct dpp_curve_params *net_access_key_curve = NULL; 4800*a90b9d01SCy Schubert 4801*a90b9d01SCy Schubert curve = get_param(cmd, " net_access_key_curve="); 4802*a90b9d01SCy Schubert if (curve) { 4803*a90b9d01SCy Schubert net_access_key_curve = dpp_get_curve_name(curve); 4804*a90b9d01SCy Schubert if (!net_access_key_curve) { 4805*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 4806*a90b9d01SCy Schubert "DPP: Unsupported net_access_key_curve: %s", 4807*a90b9d01SCy Schubert curve); 4808*a90b9d01SCy Schubert goto fail; 4809*a90b9d01SCy Schubert } 4810*a90b9d01SCy Schubert os_free(curve); 4811*a90b9d01SCy Schubert } 48124bc52338SCy Schubert 48134bc52338SCy Schubert curve = get_param(cmd, " curve="); 48144bc52338SCy Schubert key = get_param(cmd, " key="); 4815c1d255d3SCy Schubert ppkey = get_param(cmd, " ppkey="); 48164bc52338SCy Schubert 48174bc52338SCy Schubert if (key) { 48184bc52338SCy Schubert privkey_len = os_strlen(key) / 2; 48194bc52338SCy Schubert privkey = os_malloc(privkey_len); 48204bc52338SCy Schubert if (!privkey || 48214bc52338SCy Schubert hexstr2bin(key, privkey, privkey_len) < 0) 48224bc52338SCy Schubert goto fail; 48234bc52338SCy Schubert } 48244bc52338SCy Schubert 4825c1d255d3SCy Schubert if (ppkey) { 4826c1d255d3SCy Schubert pp_key_len = os_strlen(ppkey) / 2; 4827c1d255d3SCy Schubert pp_key = os_malloc(pp_key_len); 4828c1d255d3SCy Schubert if (!pp_key || 4829c1d255d3SCy Schubert hexstr2bin(ppkey, pp_key, pp_key_len) < 0) 4830c1d255d3SCy Schubert goto fail; 4831c1d255d3SCy Schubert } 4832c1d255d3SCy Schubert 4833c1d255d3SCy Schubert conf = dpp_keygen_configurator(curve, privkey, privkey_len, 4834c1d255d3SCy Schubert pp_key, pp_key_len); 48354bc52338SCy Schubert if (!conf) 48364bc52338SCy Schubert goto fail; 48374bc52338SCy Schubert 4838*a90b9d01SCy Schubert conf->net_access_key_curve = net_access_key_curve; 48394bc52338SCy Schubert conf->id = dpp_next_configurator_id(dpp); 48404bc52338SCy Schubert dl_list_add(&dpp->configurator, &conf->list); 48414bc52338SCy Schubert ret = conf->id; 48424bc52338SCy Schubert conf = NULL; 48434bc52338SCy Schubert fail: 48444bc52338SCy Schubert os_free(curve); 48454bc52338SCy Schubert str_clear_free(key); 4846c1d255d3SCy Schubert str_clear_free(ppkey); 48474bc52338SCy Schubert bin_clear_free(privkey, privkey_len); 4848c1d255d3SCy Schubert bin_clear_free(pp_key, pp_key_len); 48494bc52338SCy Schubert dpp_configurator_free(conf); 48504bc52338SCy Schubert return ret; 48514bc52338SCy Schubert } 48524bc52338SCy Schubert 48534bc52338SCy Schubert 4854*a90b9d01SCy Schubert int dpp_configurator_set(struct dpp_global *dpp, const char *cmd) 4855*a90b9d01SCy Schubert { 4856*a90b9d01SCy Schubert unsigned int id; 4857*a90b9d01SCy Schubert struct dpp_configurator *conf; 4858*a90b9d01SCy Schubert char *curve; 4859*a90b9d01SCy Schubert 4860*a90b9d01SCy Schubert id = atoi(cmd); 4861*a90b9d01SCy Schubert conf = dpp_configurator_get_id(dpp, id); 4862*a90b9d01SCy Schubert if (!conf) 4863*a90b9d01SCy Schubert return -1; 4864*a90b9d01SCy Schubert 4865*a90b9d01SCy Schubert curve = get_param(cmd, " net_access_key_curve="); 4866*a90b9d01SCy Schubert if (curve) { 4867*a90b9d01SCy Schubert const struct dpp_curve_params *net_access_key_curve; 4868*a90b9d01SCy Schubert 4869*a90b9d01SCy Schubert net_access_key_curve = dpp_get_curve_name(curve); 4870*a90b9d01SCy Schubert os_free(curve); 4871*a90b9d01SCy Schubert if (!net_access_key_curve) 4872*a90b9d01SCy Schubert return -1; 4873*a90b9d01SCy Schubert conf->net_access_key_curve = net_access_key_curve; 4874*a90b9d01SCy Schubert } 4875*a90b9d01SCy Schubert 4876*a90b9d01SCy Schubert return 0; 4877*a90b9d01SCy Schubert } 4878*a90b9d01SCy Schubert 4879*a90b9d01SCy Schubert 48804bc52338SCy Schubert static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id) 48814bc52338SCy Schubert { 48824bc52338SCy Schubert struct dpp_configurator *conf, *tmp; 48834bc52338SCy Schubert int found = 0; 48844bc52338SCy Schubert 48854bc52338SCy Schubert if (!dpp) 48864bc52338SCy Schubert return -1; 48874bc52338SCy Schubert 48884bc52338SCy Schubert dl_list_for_each_safe(conf, tmp, &dpp->configurator, 48894bc52338SCy Schubert struct dpp_configurator, list) { 48904bc52338SCy Schubert if (id && conf->id != id) 48914bc52338SCy Schubert continue; 48924bc52338SCy Schubert found = 1; 48934bc52338SCy Schubert dl_list_del(&conf->list); 48944bc52338SCy Schubert dpp_configurator_free(conf); 48954bc52338SCy Schubert } 48964bc52338SCy Schubert 48974bc52338SCy Schubert if (id == 0) 48984bc52338SCy Schubert return 0; /* flush succeeds regardless of entries found */ 48994bc52338SCy Schubert return found ? 0 : -1; 49004bc52338SCy Schubert } 49014bc52338SCy Schubert 49024bc52338SCy Schubert 49034bc52338SCy Schubert int dpp_configurator_remove(struct dpp_global *dpp, const char *id) 49044bc52338SCy Schubert { 49054bc52338SCy Schubert unsigned int id_val; 49064bc52338SCy Schubert 49074bc52338SCy Schubert if (os_strcmp(id, "*") == 0) { 49084bc52338SCy Schubert id_val = 0; 49094bc52338SCy Schubert } else { 49104bc52338SCy Schubert id_val = atoi(id); 49114bc52338SCy Schubert if (id_val == 0) 49124bc52338SCy Schubert return -1; 49134bc52338SCy Schubert } 49144bc52338SCy Schubert 49154bc52338SCy Schubert return dpp_configurator_del(dpp, id_val); 49164bc52338SCy Schubert } 49174bc52338SCy Schubert 49184bc52338SCy Schubert 49194bc52338SCy Schubert int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, 49204bc52338SCy Schubert char *buf, size_t buflen) 49214bc52338SCy Schubert { 49224bc52338SCy Schubert struct dpp_configurator *conf; 49234bc52338SCy Schubert 49244bc52338SCy Schubert conf = dpp_configurator_get_id(dpp, id); 49254bc52338SCy Schubert if (!conf) 49264bc52338SCy Schubert return -1; 49274bc52338SCy Schubert 49284bc52338SCy Schubert return dpp_configurator_get_key(conf, buf, buflen); 49294bc52338SCy Schubert } 49304bc52338SCy Schubert 49314bc52338SCy Schubert 4932206b73d0SCy Schubert #ifdef CONFIG_DPP2 4933206b73d0SCy Schubert 4934c1d255d3SCy Schubert int dpp_configurator_from_backup(struct dpp_global *dpp, 4935c1d255d3SCy Schubert struct dpp_asymmetric_key *key) 4936206b73d0SCy Schubert { 4937c1d255d3SCy Schubert struct dpp_configurator *conf; 49384b72b91aSCy Schubert const struct dpp_curve_params *curve, *curve_pp; 4939c1d255d3SCy Schubert 4940c1d255d3SCy Schubert if (!key->csign || !key->pp_key) 4941c1d255d3SCy Schubert return -1; 49424b72b91aSCy Schubert 49434b72b91aSCy Schubert curve = dpp_get_curve_ike_group(crypto_ec_key_group(key->csign)); 4944c1d255d3SCy Schubert if (!curve) { 4945c1d255d3SCy Schubert wpa_printf(MSG_INFO, "DPP: Unsupported group in c-sign-key"); 4946c1d255d3SCy Schubert return -1; 4947206b73d0SCy Schubert } 49484b72b91aSCy Schubert 49494b72b91aSCy Schubert curve_pp = dpp_get_curve_ike_group(crypto_ec_key_group(key->pp_key)); 49504b72b91aSCy Schubert if (!curve_pp) { 49514b72b91aSCy Schubert wpa_printf(MSG_INFO, "DPP: Unsupported group in ppKey"); 4952c1d255d3SCy Schubert return -1; 49534b72b91aSCy Schubert } 49544b72b91aSCy Schubert 49554b72b91aSCy Schubert if (curve != curve_pp) { 4956c1d255d3SCy Schubert wpa_printf(MSG_INFO, 4957c1d255d3SCy Schubert "DPP: Mismatch in c-sign-key and ppKey groups"); 4958c1d255d3SCy Schubert return -1; 4959c1d255d3SCy Schubert } 4960c1d255d3SCy Schubert 4961c1d255d3SCy Schubert conf = os_zalloc(sizeof(*conf)); 4962c1d255d3SCy Schubert if (!conf) 4963c1d255d3SCy Schubert return -1; 4964c1d255d3SCy Schubert conf->curve = curve; 4965c1d255d3SCy Schubert conf->csign = key->csign; 4966c1d255d3SCy Schubert key->csign = NULL; 4967c1d255d3SCy Schubert conf->pp_key = key->pp_key; 4968c1d255d3SCy Schubert key->pp_key = NULL; 4969c1d255d3SCy Schubert conf->own = 1; 4970c1d255d3SCy Schubert if (dpp_configurator_gen_kid(conf) < 0) { 4971c1d255d3SCy Schubert dpp_configurator_free(conf); 4972c1d255d3SCy Schubert return -1; 4973c1d255d3SCy Schubert } 4974c1d255d3SCy Schubert 4975c1d255d3SCy Schubert conf->id = dpp_next_configurator_id(dpp); 4976c1d255d3SCy Schubert dl_list_add(&dpp->configurator, &conf->list); 4977c1d255d3SCy Schubert return conf->id; 4978206b73d0SCy Schubert } 4979206b73d0SCy Schubert 4980206b73d0SCy Schubert 4981c1d255d3SCy Schubert struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp, 4982c1d255d3SCy Schubert const u8 *kid) 4983206b73d0SCy Schubert { 4984c1d255d3SCy Schubert struct dpp_configurator *conf; 4985206b73d0SCy Schubert 4986206b73d0SCy Schubert if (!dpp) 4987c1d255d3SCy Schubert return NULL; 4988206b73d0SCy Schubert 4989c1d255d3SCy Schubert dl_list_for_each(conf, &dpp->configurator, 4990c1d255d3SCy Schubert struct dpp_configurator, list) { 4991c1d255d3SCy Schubert if (os_memcmp(conf->kid_hash, kid, SHA256_MAC_LEN) == 0) 4992c1d255d3SCy Schubert return conf; 4993206b73d0SCy Schubert } 4994c1d255d3SCy Schubert return NULL; 4995206b73d0SCy Schubert } 4996206b73d0SCy Schubert 4997206b73d0SCy Schubert #endif /* CONFIG_DPP2 */ 4998206b73d0SCy Schubert 4999206b73d0SCy Schubert 5000206b73d0SCy Schubert struct dpp_global * dpp_global_init(struct dpp_global_config *config) 50014bc52338SCy Schubert { 50024bc52338SCy Schubert struct dpp_global *dpp; 50034bc52338SCy Schubert 50044bc52338SCy Schubert dpp = os_zalloc(sizeof(*dpp)); 50054bc52338SCy Schubert if (!dpp) 50064bc52338SCy Schubert return NULL; 5007206b73d0SCy Schubert #ifdef CONFIG_DPP2 5008206b73d0SCy Schubert dpp->cb_ctx = config->cb_ctx; 5009c1d255d3SCy Schubert dpp->remove_bi = config->remove_bi; 5010206b73d0SCy Schubert #endif /* CONFIG_DPP2 */ 50114bc52338SCy Schubert 50124bc52338SCy Schubert dl_list_init(&dpp->bootstrap); 50134bc52338SCy Schubert dl_list_init(&dpp->configurator); 5014206b73d0SCy Schubert #ifdef CONFIG_DPP2 5015206b73d0SCy Schubert dl_list_init(&dpp->controllers); 5016206b73d0SCy Schubert dl_list_init(&dpp->tcp_init); 5017*a90b9d01SCy Schubert dpp->relay_sock = -1; 5018206b73d0SCy Schubert #endif /* CONFIG_DPP2 */ 50194bc52338SCy Schubert 50204bc52338SCy Schubert return dpp; 50214bc52338SCy Schubert } 50224bc52338SCy Schubert 50234bc52338SCy Schubert 50244bc52338SCy Schubert void dpp_global_clear(struct dpp_global *dpp) 50254bc52338SCy Schubert { 50264bc52338SCy Schubert if (!dpp) 50274bc52338SCy Schubert return; 50284bc52338SCy Schubert 50294bc52338SCy Schubert dpp_bootstrap_del(dpp, 0); 50304bc52338SCy Schubert dpp_configurator_del(dpp, 0); 5031206b73d0SCy Schubert #ifdef CONFIG_DPP2 5032206b73d0SCy Schubert dpp_tcp_init_flush(dpp); 5033206b73d0SCy Schubert dpp_relay_flush_controllers(dpp); 5034206b73d0SCy Schubert dpp_controller_stop(dpp); 5035206b73d0SCy Schubert #endif /* CONFIG_DPP2 */ 50364bc52338SCy Schubert } 50374bc52338SCy Schubert 50384bc52338SCy Schubert 50394bc52338SCy Schubert void dpp_global_deinit(struct dpp_global *dpp) 50404bc52338SCy Schubert { 50414bc52338SCy Schubert dpp_global_clear(dpp); 50424bc52338SCy Schubert os_free(dpp); 50434bc52338SCy Schubert } 5044206b73d0SCy Schubert 5045206b73d0SCy Schubert 5046*a90b9d01SCy Schubert void dpp_notify_auth_success(struct dpp_authentication *auth, int initiator) 5047*a90b9d01SCy Schubert { 5048*a90b9d01SCy Schubert u8 hash[SHA256_MAC_LEN]; 5049*a90b9d01SCy Schubert char hex[SHA256_MAC_LEN * 2 + 1]; 5050*a90b9d01SCy Schubert 5051*a90b9d01SCy Schubert if (auth->peer_protocol_key) { 5052*a90b9d01SCy Schubert dpp_get_pubkey_hash(auth->peer_protocol_key, hash); 5053*a90b9d01SCy Schubert wpa_snprintf_hex(hex, sizeof(hex), hash, sizeof(hash)); 5054*a90b9d01SCy Schubert } else { 5055*a90b9d01SCy Schubert hex[0] = '\0'; 5056*a90b9d01SCy Schubert } 5057*a90b9d01SCy Schubert wpa_msg(auth->msg_ctx, MSG_INFO, 5058*a90b9d01SCy Schubert DPP_EVENT_AUTH_SUCCESS "init=%d pkhash=%s own=%d peer=%d", 5059*a90b9d01SCy Schubert initiator, hex, auth->own_bi ? (int) auth->own_bi->id : -1, 5060*a90b9d01SCy Schubert auth->peer_bi ? (int) auth->peer_bi->id : -1); 5061*a90b9d01SCy Schubert } 5062*a90b9d01SCy Schubert 5063*a90b9d01SCy Schubert 5064206b73d0SCy Schubert #ifdef CONFIG_DPP2 5065206b73d0SCy Schubert 5066c1d255d3SCy Schubert struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi) 5067206b73d0SCy Schubert { 5068206b73d0SCy Schubert struct wpabuf *msg; 5069206b73d0SCy Schubert 5070c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "DPP: Build Presence Announcement frame"); 5071c1d255d3SCy Schubert 5072c1d255d3SCy Schubert msg = dpp_alloc_msg(DPP_PA_PRESENCE_ANNOUNCEMENT, 4 + SHA256_MAC_LEN); 5073206b73d0SCy Schubert if (!msg) 5074206b73d0SCy Schubert return NULL; 5075c1d255d3SCy Schubert 5076c1d255d3SCy Schubert /* Responder Bootstrapping Key Hash */ 5077c1d255d3SCy Schubert dpp_build_attr_r_bootstrap_key_hash(msg, bi->pubkey_hash_chirp); 5078c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 5079c1d255d3SCy Schubert "DPP: Presence Announcement frame attributes", msg); 5080206b73d0SCy Schubert return msg; 5081206b73d0SCy Schubert } 5082206b73d0SCy Schubert 5083206b73d0SCy Schubert 5084c1d255d3SCy Schubert void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src, 5085c1d255d3SCy Schubert unsigned int freq, const u8 *hash) 5086206b73d0SCy Schubert { 5087c1d255d3SCy Schubert char hex[SHA256_MAC_LEN * 2 + 1]; 5088206b73d0SCy Schubert 5089c1d255d3SCy Schubert wpa_snprintf_hex(hex, sizeof(hex), hash, SHA256_MAC_LEN); 5090c1d255d3SCy Schubert wpa_msg(msg_ctx, MSG_INFO, 5091c1d255d3SCy Schubert DPP_EVENT_CHIRP_RX "id=%d src=" MACSTR " freq=%u hash=%s", 5092c1d255d3SCy Schubert id, MAC2STR(src), freq, hex); 5093206b73d0SCy Schubert } 5094206b73d0SCy Schubert 5095206b73d0SCy Schubert #endif /* CONFIG_DPP2 */ 5096*a90b9d01SCy Schubert 5097*a90b9d01SCy Schubert 5098*a90b9d01SCy Schubert #ifdef CONFIG_DPP3 5099*a90b9d01SCy Schubert 5100*a90b9d01SCy Schubert struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi) 5101*a90b9d01SCy Schubert { 5102*a90b9d01SCy Schubert struct wpabuf *msg; 5103*a90b9d01SCy Schubert const u8 *r_hash = bi->pubkey_hash_chirp; 5104*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 5105*a90b9d01SCy Schubert u8 test_hash[SHA256_MAC_LEN]; 5106*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 5107*a90b9d01SCy Schubert 5108*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 5109*a90b9d01SCy Schubert "DPP: Build Push Button Presence Announcement frame"); 5110*a90b9d01SCy Schubert 5111*a90b9d01SCy Schubert msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT, 5112*a90b9d01SCy Schubert 4 + SHA256_MAC_LEN); 5113*a90b9d01SCy Schubert if (!msg) 5114*a90b9d01SCy Schubert return NULL; 5115*a90b9d01SCy Schubert 5116*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 5117*a90b9d01SCy Schubert if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ) { 5118*a90b9d01SCy Schubert wpa_printf(MSG_INFO, 5119*a90b9d01SCy Schubert "DPP: TESTING - invalid R-Bootstrap Key Hash"); 5120*a90b9d01SCy Schubert os_memcpy(test_hash, r_hash, SHA256_MAC_LEN); 5121*a90b9d01SCy Schubert test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 5122*a90b9d01SCy Schubert r_hash = test_hash; 5123*a90b9d01SCy Schubert } 5124*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 5125*a90b9d01SCy Schubert 5126*a90b9d01SCy Schubert /* Responder Bootstrapping Key Hash */ 5127*a90b9d01SCy Schubert dpp_build_attr_r_bootstrap_key_hash(msg, r_hash); 5128*a90b9d01SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 5129*a90b9d01SCy Schubert "DPP: Push Button Presence Announcement frame attributes", 5130*a90b9d01SCy Schubert msg); 5131*a90b9d01SCy Schubert return msg; 5132*a90b9d01SCy Schubert } 5133*a90b9d01SCy Schubert 5134*a90b9d01SCy Schubert 5135*a90b9d01SCy Schubert struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi, 5136*a90b9d01SCy Schubert const u8 *e_hash, 5137*a90b9d01SCy Schubert const u8 *c_nonce, 5138*a90b9d01SCy Schubert size_t c_nonce_len) 5139*a90b9d01SCy Schubert { 5140*a90b9d01SCy Schubert struct wpabuf *msg; 5141*a90b9d01SCy Schubert const u8 *i_hash = bi->pubkey_hash_chirp; 5142*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 5143*a90b9d01SCy Schubert u8 test_hash[SHA256_MAC_LEN]; 5144*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 5145*a90b9d01SCy Schubert 5146*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 5147*a90b9d01SCy Schubert "DPP: Build Push Button Presence Announcement Response frame"); 5148*a90b9d01SCy Schubert 5149*a90b9d01SCy Schubert msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP, 5150*a90b9d01SCy Schubert 2 * (4 + SHA256_MAC_LEN) + 4 + c_nonce_len); 5151*a90b9d01SCy Schubert if (!msg) 5152*a90b9d01SCy Schubert return NULL; 5153*a90b9d01SCy Schubert 5154*a90b9d01SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 5155*a90b9d01SCy Schubert if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_PB_RESP) { 5156*a90b9d01SCy Schubert wpa_printf(MSG_INFO, 5157*a90b9d01SCy Schubert "DPP: TESTING - invalid I-Bootstrap Key Hash"); 5158*a90b9d01SCy Schubert os_memcpy(test_hash, i_hash, SHA256_MAC_LEN); 5159*a90b9d01SCy Schubert test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 5160*a90b9d01SCy Schubert i_hash = test_hash; 5161*a90b9d01SCy Schubert } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_RESP) { 5162*a90b9d01SCy Schubert wpa_printf(MSG_INFO, 5163*a90b9d01SCy Schubert "DPP: TESTING - invalid R-Bootstrap Key Hash"); 5164*a90b9d01SCy Schubert os_memcpy(test_hash, e_hash, SHA256_MAC_LEN); 5165*a90b9d01SCy Schubert test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 5166*a90b9d01SCy Schubert e_hash = test_hash; 5167*a90b9d01SCy Schubert } 5168*a90b9d01SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 5169*a90b9d01SCy Schubert 5170*a90b9d01SCy Schubert /* Initiator Bootstrapping Key Hash */ 5171*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); 5172*a90b9d01SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); 5173*a90b9d01SCy Schubert wpabuf_put_le16(msg, SHA256_MAC_LEN); 5174*a90b9d01SCy Schubert wpabuf_put_data(msg, i_hash, SHA256_MAC_LEN); 5175*a90b9d01SCy Schubert 5176*a90b9d01SCy Schubert /* Responder Bootstrapping Key Hash */ 5177*a90b9d01SCy Schubert dpp_build_attr_r_bootstrap_key_hash(msg, e_hash); 5178*a90b9d01SCy Schubert 5179*a90b9d01SCy Schubert /* Configurator Nonce */ 5180*a90b9d01SCy Schubert wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE); 5181*a90b9d01SCy Schubert wpabuf_put_le16(msg, c_nonce_len); 5182*a90b9d01SCy Schubert wpabuf_put_data(msg, c_nonce, c_nonce_len); 5183*a90b9d01SCy Schubert 5184*a90b9d01SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 5185*a90b9d01SCy Schubert "DPP: Push Button Presence Announcement Response frame attributes", 5186*a90b9d01SCy Schubert msg); 5187*a90b9d01SCy Schubert return msg; 5188*a90b9d01SCy Schubert } 5189*a90b9d01SCy Schubert 5190*a90b9d01SCy Schubert #endif /* CONFIG_DPP3 */ 5191