/* * DPP reconfiguration * Copyright (c) 2020, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/json.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/aes.h" #include "crypto/aes_siv.h" #include "dpp.h" #include "dpp_i.h" #ifdef CONFIG_DPP2 static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash) { if (hash) { wpa_printf(MSG_DEBUG, "DPP: Configurator C-sign key Hash"); wpabuf_put_le16(msg, DPP_ATTR_C_SIGN_KEY_HASH); wpabuf_put_le16(msg, SHA256_MAC_LEN); wpabuf_put_data(msg, hash, SHA256_MAC_LEN); } } struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key, size_t csign_key_len, const u8 *net_access_key, size_t net_access_key_len, struct dpp_reconfig_id *id) { struct wpabuf *msg = NULL; struct crypto_ec_key *csign = NULL; struct wpabuf *uncomp; u8 hash[SHA256_MAC_LEN]; const u8 *addr[1]; size_t len[1]; int res; size_t attr_len; const struct dpp_curve_params *own_curve; struct crypto_ec_key *own_key; struct wpabuf *a_nonce = NULL, *e_id = NULL; wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame"); own_key = dpp_set_keypair(&own_curve, net_access_key, net_access_key_len); if (!own_key) { wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); goto fail; } csign = crypto_ec_key_parse_pub(csign_key, csign_key_len); if (!csign) { wpa_printf(MSG_ERROR, "DPP: Failed to parse local C-sign-key information"); goto fail; } uncomp = crypto_ec_key_get_pubkey_point(csign, 1); crypto_ec_key_deinit(csign); if (!uncomp) goto fail; addr[0] = wpabuf_head(uncomp); len[0] = wpabuf_len(uncomp); wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]); res = sha256_vector(1, addr, len, hash); wpabuf_free(uncomp); if (res < 0) goto fail; wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)", hash, SHA256_MAC_LEN); if (dpp_update_reconfig_id(id) < 0) { wpa_printf(MSG_ERROR, "DPP: Failed to generate E'-id"); goto fail; } a_nonce = crypto_ec_key_get_pubkey_point(id->a_nonce, 0); e_id = crypto_ec_key_get_pubkey_point(id->e_prime_id, 0); if (!a_nonce || !e_id) goto fail; attr_len = 4 + SHA256_MAC_LEN; attr_len += 4 + 2; attr_len += 4 + wpabuf_len(a_nonce); attr_len += 4 + wpabuf_len(e_id); msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, attr_len); if (!msg) goto fail; /* Configurator C-sign key Hash */ dpp_build_attr_csign_key_hash(msg, hash); /* Finite Cyclic Group attribute */ wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u", own_curve->ike_group); wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); wpabuf_put_le16(msg, 2); wpabuf_put_le16(msg, own_curve->ike_group); /* A-NONCE */ wpabuf_put_le16(msg, DPP_ATTR_A_NONCE); wpabuf_put_le16(msg, wpabuf_len(a_nonce)); wpabuf_put_buf(msg, a_nonce); /* E'-id */ wpabuf_put_le16(msg, DPP_ATTR_E_PRIME_ID); wpabuf_put_le16(msg, wpabuf_len(e_id)); wpabuf_put_buf(msg, e_id); wpa_hexdump_buf(MSG_DEBUG, "DPP: Reconfig Announcement frame attributes", msg); fail: wpabuf_free(a_nonce); wpabuf_free(e_id); crypto_ec_key_deinit(own_key); return msg; } static struct wpabuf * dpp_reconfig_build_req(struct dpp_authentication *auth) { struct wpabuf *msg; size_t attr_len; u8 ver = DPP_VERSION; /* Build DPP Reconfig Authentication Request frame attributes */ attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) + 4 + auth->curve->nonce_len; msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_REQ, attr_len); if (!msg) return NULL; /* Transaction ID */ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); wpabuf_put_le16(msg, 1); wpabuf_put_u8(msg, auth->transaction_id); #ifdef CONFIG_TESTING_OPTIONS if (dpp_test == DPP_TEST_NO_PROTOCOL_VERSION_RECONFIG_AUTH_REQ) { wpa_printf(MSG_INFO, "DPP: TESTING - no Protocol Version"); goto skip_proto_ver; } if (dpp_test == DPP_TEST_INVALID_PROTOCOL_VERSION_RECONFIG_AUTH_REQ) { wpa_printf(MSG_INFO, "DPP: TESTING - invalid Protocol Version"); ver = 1; } #endif /* CONFIG_TESTING_OPTIONS */ /* Protocol Version */ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); wpabuf_put_le16(msg, 1); wpabuf_put_u8(msg, ver); #ifdef CONFIG_TESTING_OPTIONS skip_proto_ver: #endif /* CONFIG_TESTING_OPTIONS */ /* DPP Connector */ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); wpabuf_put_le16(msg, os_strlen(auth->conf->connector)); wpabuf_put_str(msg, auth->conf->connector); /* C-nonce */ wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE); wpabuf_put_le16(msg, auth->curve->nonce_len); wpabuf_put_data(msg, auth->c_nonce, auth->curve->nonce_len); wpa_hexdump_buf(MSG_DEBUG, "DPP: Reconfig Authentication Request frame attributes", msg); return msg; } static int dpp_configurator_build_own_connector(struct dpp_configurator *conf, const struct dpp_curve_params *curve) { struct wpabuf *dppcon = NULL; int ret = -1; if (conf->connector) return 0; /* already generated */ wpa_printf(MSG_DEBUG, "DPP: Sign own Configurator Connector for reconfiguration with curve %s", conf->curve->name); conf->connector_key = dpp_gen_keypair(curve); if (!conf->connector_key) goto fail; /* Connector (JSON dppCon object) */ dppcon = wpabuf_alloc(1000 + 2 * curve->prime_len * 4 / 3); if (!dppcon) goto fail; json_start_object(dppcon, NULL); json_start_array(dppcon, "groups"); json_start_object(dppcon, NULL); json_add_string(dppcon, "groupId", "*"); json_value_sep(dppcon); json_add_string(dppcon, "netRole", "configurator"); json_end_object(dppcon); json_end_array(dppcon); json_value_sep(dppcon); if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL, curve) < 0) { wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); goto fail; } json_end_object(dppcon); wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", (const char *) wpabuf_head(dppcon)); conf->connector = dpp_sign_connector(conf, dppcon); if (!conf->connector) goto fail; wpa_printf(MSG_DEBUG, "DPP: signedConnector: %s", conf->connector); ret = 0; fail: wpabuf_free(dppcon); return ret; } struct dpp_authentication * dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx, struct dpp_configurator *conf, unsigned int freq, u16 group, const u8 *a_nonce_attr, size_t a_nonce_len, const u8 *e_id_attr, size_t e_id_len) { struct dpp_authentication *auth; const struct dpp_curve_params *curve; struct crypto_ec_key *a_nonce, *e_prime_id; struct crypto_ec_point *e_id; curve = dpp_get_curve_ike_group(group); if (!curve) { wpa_printf(MSG_DEBUG, "DPP: Unsupported group %u - cannot reconfigure", group); return NULL; } if (!a_nonce_attr) { wpa_printf(MSG_INFO, "DPP: Missing required A-NONCE attribute"); return NULL; } wpa_hexdump(MSG_MSGDUMP, "DPP: A-NONCE", a_nonce_attr, a_nonce_len); a_nonce = dpp_set_pubkey_point(conf->csign, a_nonce_attr, a_nonce_len); if (!a_nonce) { wpa_printf(MSG_INFO, "DPP: Invalid A-NONCE"); return NULL; } dpp_debug_print_key("A-NONCE", a_nonce); if (!e_id_attr) { wpa_printf(MSG_INFO, "DPP: Missing required E'-id attribute"); return NULL; } e_prime_id = dpp_set_pubkey_point(conf->csign, e_id_attr, e_id_len); if (!e_prime_id) { wpa_printf(MSG_INFO, "DPP: Invalid E'-id"); crypto_ec_key_deinit(a_nonce); return NULL; } dpp_debug_print_key("E'-id", e_prime_id); e_id = dpp_decrypt_e_id(conf->pp_key, a_nonce, e_prime_id); crypto_ec_key_deinit(a_nonce); crypto_ec_key_deinit(e_prime_id); if (!e_id) { wpa_printf(MSG_INFO, "DPP: Could not decrypt E'-id"); return NULL; } /* TODO: could use E-id to determine whether reconfiguration with this * Enrollee has already been started and is waiting for updated * configuration instead of replying again before such configuration * becomes available */ crypto_ec_point_deinit(e_id, 1); auth = dpp_alloc_auth(dpp, msg_ctx); if (!auth) return NULL; auth->conf = conf; auth->reconfig = 1; auth->initiator = 1; auth->waiting_auth_resp = 1; auth->allowed_roles = DPP_CAPAB_CONFIGURATOR; auth->configurator = 1; auth->curve = curve; auth->transaction_id = 1; if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0) goto fail; if (dpp_configurator_build_own_connector(conf, curve) < 0) goto fail; if (random_get_bytes(auth->c_nonce, auth->curve->nonce_len)) { wpa_printf(MSG_ERROR, "DPP: Failed to generate C-nonce"); goto fail; } auth->reconfig_req_msg = dpp_reconfig_build_req(auth); if (!auth->reconfig_req_msg) goto fail; out: return auth; fail: dpp_auth_deinit(auth); auth = NULL; goto out; } static int dpp_reconfig_build_resp(struct dpp_authentication *auth, const char *own_connector, struct wpabuf *conn_status) { struct wpabuf *msg = NULL, *clear, *pr = NULL; u8 *attr_start, *attr_end; size_t clear_len, attr_len, len[2]; const u8 *addr[2]; u8 *wrapped; int res = -1; /* Build DPP Reconfig Authentication Response frame attributes */ clear_len = 4 + auth->curve->nonce_len + 4 + wpabuf_len(conn_status); clear = wpabuf_alloc(clear_len); if (!clear) goto fail; /* C-nonce (wrapped) */ wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE); wpabuf_put_le16(clear, auth->curve->nonce_len); wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len); /* Connection Status (wrapped) */ wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS); wpabuf_put_le16(clear, wpabuf_len(conn_status)); wpabuf_put_buf(clear, conn_status); pr = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0); if (!pr) goto fail; attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(own_connector) + 4 + auth->curve->nonce_len + 4 + wpabuf_len(pr) + 4 + wpabuf_len(clear) + AES_BLOCK_SIZE; msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len); if (!msg) goto fail; attr_start = wpabuf_put(msg, 0); /* Transaction ID */ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); wpabuf_put_le16(msg, 1); wpabuf_put_u8(msg, auth->transaction_id); /* Protocol Version */ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); wpabuf_put_le16(msg, 1); wpabuf_put_u8(msg, DPP_VERSION); /* R-Connector */ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); wpabuf_put_le16(msg, os_strlen(own_connector)); wpabuf_put_str(msg, own_connector); /* E-nonce */ wpabuf_put_le16(msg, DPP_ATTR_ENROLLEE_NONCE); wpabuf_put_le16(msg, auth->curve->nonce_len); wpabuf_put_data(msg, auth->e_nonce, auth->curve->nonce_len); /* Responder Protocol Key (Pr) */ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY); wpabuf_put_le16(msg, wpabuf_len(pr)); wpabuf_put_buf(msg, pr); attr_end = wpabuf_put(msg, 0); /* OUI, OUI type, Crypto Suite, DPP frame type */ addr[0] = wpabuf_head_u8(msg) + 2; len[0] = 3 + 1 + 1 + 1; wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); /* Attributes before Wrapped Data */ addr[1] = attr_start; len[1] = attr_end - attr_start; wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); /* Wrapped Data: {C-nonce, E-nonce, Connection Status}ke */ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, wpabuf_head(clear), wpabuf_len(clear), 2, addr, len, wrapped) < 0) goto fail; wpa_hexdump_buf(MSG_DEBUG, "DPP: Reconfig Authentication Response frame attributes", msg); wpabuf_free(auth->reconfig_resp_msg); auth->reconfig_resp_msg = msg; res = 0; out: wpabuf_free(clear); wpabuf_free(pr); return res; fail: wpabuf_free(msg); goto out; } struct dpp_authentication * dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, const char *own_connector, const u8 *net_access_key, size_t net_access_key_len, const u8 *csign_key, size_t csign_key_len, unsigned int freq, const u8 *hdr, const u8 *attr_start, size_t attr_len) { struct dpp_authentication *auth = NULL; const u8 *trans_id, *version, *i_connector, *c_nonce; u16 trans_id_len, version_len, i_connector_len, c_nonce_len; struct dpp_signed_connector_info info; enum dpp_status_error res; struct json_token *root = NULL, *own_root = NULL, *token; unsigned char *own_conn = NULL; struct wpabuf *conn_status = NULL; os_memset(&info, 0, sizeof(info)); trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID, &trans_id_len); if (!trans_id || trans_id_len != 1) { wpa_printf(MSG_DEBUG, "DPP: Peer did not include Transaction ID"); goto fail; } version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, &version_len); if (!version || version_len < 1 || version[0] < 2) { wpa_printf(MSG_DEBUG, "DPP: Missing or invalid Protocol Version attribute"); goto fail; } i_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR, &i_connector_len); if (!i_connector) { wpa_printf(MSG_DEBUG, "DPP: Missing I-Connector attribute"); goto fail; } wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector", i_connector, i_connector_len); c_nonce = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len); if (!c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) { wpa_printf(MSG_DEBUG, "DPP: Missing or invalid C-nonce attribute"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len); res = dpp_check_signed_connector(&info, csign_key, csign_key_len, i_connector, i_connector_len); if (res != DPP_STATUS_OK) { wpa_printf(MSG_DEBUG, "DPP: Invalid I-Connector"); goto fail; } root = json_parse((const char *) info.payload, info.payload_len); own_root = dpp_parse_own_connector(own_connector); if (!root || !own_root || !dpp_connector_match_groups(own_root, root, true)) { wpa_printf(MSG_DEBUG, "DPP: I-Connector does not include compatible group netrole with own connector"); goto fail; } token = json_get_member(root, "expiry"); if (token && token->type == JSON_STRING && dpp_key_expired(token->string, NULL)) { wpa_printf(MSG_DEBUG, "DPP: I-Connector (netAccessKey) has expired"); goto fail; } token = json_get_member(root, "netAccessKey"); if (!token || token->type != JSON_OBJECT) { wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); goto fail; } auth = dpp_alloc_auth(dpp, msg_ctx); if (!auth) return NULL; auth->reconfig = 1; auth->allowed_roles = DPP_CAPAB_ENROLLEE; if (dpp_prepare_channel_list(auth, freq, NULL, 0) < 0) goto fail; auth->transaction_id = trans_id[0]; auth->peer_version = version[0]; wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", auth->peer_version); os_memcpy(auth->c_nonce, c_nonce, c_nonce_len); if (dpp_reconfig_derive_ke_responder(auth, net_access_key, net_access_key_len, token) < 0) goto fail; if (c_nonce_len != auth->curve->nonce_len) { wpa_printf(MSG_DEBUG, "DPP: Unexpected C-nonce length %u (curve nonce len %zu)", c_nonce_len, auth->curve->nonce_len); goto fail; } /* Build Connection Status object */ /* TODO: Get appropriate result value */ /* TODO: ssid64 and channelList */ conn_status = dpp_build_conn_status(DPP_STATUS_NO_AP, NULL, 0, NULL); if (!conn_status) goto fail; if (dpp_reconfig_build_resp(auth, own_connector, conn_status) < 0) goto fail; out: os_free(info.payload); os_free(own_conn); json_free(root); json_free(own_root); wpabuf_free(conn_status); return auth; fail: dpp_auth_deinit(auth); auth = NULL; goto out; } struct wpabuf * dpp_reconfig_build_conf(struct dpp_authentication *auth) { struct wpabuf *msg = NULL, *clear; u8 *attr_start, *attr_end; size_t clear_len, attr_len, len[2]; const u8 *addr[2]; u8 *wrapped; u8 flags; /* Build DPP Reconfig Authentication Confirm frame attributes */ clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) + 4 + 1; clear = wpabuf_alloc(clear_len); if (!clear) goto fail; /* Transaction ID */ wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID); wpabuf_put_le16(clear, 1); wpabuf_put_u8(clear, auth->transaction_id); /* Protocol Version */ wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION); wpabuf_put_le16(clear, 1); wpabuf_put_u8(clear, auth->peer_version); /* C-nonce (wrapped) */ wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE); wpabuf_put_le16(clear, auth->curve->nonce_len); wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len); /* E-nonce (wrapped) */ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); wpabuf_put_le16(clear, auth->curve->nonce_len); wpabuf_put_data(clear, auth->e_nonce, auth->curve->nonce_len); /* Reconfig-Flags (wrapped) */ flags = DPP_CONFIG_REPLACEKEY; wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS); wpabuf_put_le16(clear, 1); wpabuf_put_u8(clear, flags); attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE; attr_len += 4 + 1; msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len); if (!msg) goto fail; attr_start = wpabuf_put(msg, 0); /* DPP Status */ dpp_build_attr_status(msg, DPP_STATUS_OK); attr_end = wpabuf_put(msg, 0); /* OUI, OUI type, Crypto Suite, DPP frame type */ addr[0] = wpabuf_head_u8(msg) + 2; len[0] = 3 + 1 + 1 + 1; wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); /* Attributes before Wrapped Data */ addr[1] = attr_start; len[1] = attr_end - attr_start; wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); /* Wrapped Data */ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, wpabuf_head(clear), wpabuf_len(clear), 2, addr, len, wrapped) < 0) goto fail; wpa_hexdump_buf(MSG_DEBUG, "DPP: Reconfig Authentication Confirm frame attributes", msg); out: wpabuf_free(clear); return msg; fail: wpabuf_free(msg); msg = NULL; goto out; } struct wpabuf * dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len) { const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data, *c_nonce, *e_nonce, *conn_status; u16 trans_id_len, version_len, r_connector_len, r_proto_len, wrapped_data_len, c_nonce_len, e_nonce_len, conn_status_len; struct wpabuf *conf = NULL; char *signed_connector = NULL; struct dpp_signed_connector_info info; enum dpp_status_error res; struct json_token *root = NULL, *token, *conn_status_json = NULL; const u8 *addr[2]; size_t len[2]; u8 *unwrapped = NULL; size_t unwrapped_len = 0; os_memset(&info, 0, sizeof(info)); if (!auth->reconfig || !auth->configurator) goto fail; wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, &wrapped_data_len); if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { dpp_auth_fail(auth, "Missing or invalid required Wrapped Data attribute"); goto fail; } wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", wrapped_data, wrapped_data_len); attr_len = wrapped_data - 4 - attr_start; trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID, &trans_id_len); if (!trans_id || trans_id_len != 1) { dpp_auth_fail(auth, "Peer did not include Transaction ID"); goto fail; } if (trans_id[0] != auth->transaction_id) { dpp_auth_fail(auth, "Transaction ID mismatch"); goto fail; } version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, &version_len); if (!version || version_len < 1 || version[0] < 2) { dpp_auth_fail(auth, "Missing or invalid Protocol Version attribute"); goto fail; } auth->peer_version = version[0]; wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", auth->peer_version); r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR, &r_connector_len); if (!r_connector) { dpp_auth_fail(auth, " Missing R-Connector attribute"); goto fail; } wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector", r_connector, r_connector_len); e_nonce = dpp_get_attr(attr_start, attr_len, DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len); if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { dpp_auth_fail(auth, "Missing or invalid E-nonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len); os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, &r_proto_len); if (!r_proto) { dpp_auth_fail(auth, "Missing required Responder Protocol Key attribute"); goto fail; } wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", r_proto, r_proto_len); signed_connector = os_malloc(r_connector_len + 1); if (!signed_connector) goto fail; os_memcpy(signed_connector, r_connector, r_connector_len); signed_connector[r_connector_len] = '\0'; res = dpp_process_signed_connector(&info, auth->conf->csign, signed_connector); if (res != DPP_STATUS_OK) { dpp_auth_fail(auth, "Invalid R-Connector"); goto fail; } root = json_parse((const char *) info.payload, info.payload_len); if (!root) { dpp_auth_fail(auth, "Invalid Connector payload"); goto fail; } /* Do not check netAccessKey expiration for reconfiguration to allow * expired Connector to be updated. */ token = json_get_member(root, "netAccessKey"); if (!token || token->type != JSON_OBJECT) { dpp_auth_fail(auth, "No netAccessKey object found"); goto fail; } if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len, token) < 0) goto fail; addr[0] = hdr; len[0] = DPP_HDR_LEN; addr[1] = attr_start; len[1] = attr_len; wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", wrapped_data, wrapped_data_len); unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; unwrapped = os_malloc(unwrapped_len); if (!unwrapped) goto fail; if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, wrapped_data, wrapped_data_len, 2, addr, len, unwrapped) < 0) { dpp_auth_fail(auth, "AES-SIV decryption failed"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", unwrapped, unwrapped_len); if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); goto fail; } c_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len); if (!c_nonce || c_nonce_len != auth->curve->nonce_len || os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) { dpp_auth_fail(auth, "Missing or invalid C-nonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len); conn_status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONN_STATUS, &conn_status_len); if (!conn_status) { dpp_auth_fail(auth, "Missing Connection Status attribute"); goto fail; } wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus", conn_status, conn_status_len); conn_status_json = json_parse((const char *) conn_status, conn_status_len); if (!conn_status_json) { dpp_auth_fail(auth, "Could not parse connStatus"); goto fail; } /* TODO: use connStatus information */ conf = dpp_reconfig_build_conf(auth); if (conf) auth->reconfig_success = true; out: json_free(root); json_free(conn_status_json); bin_clear_free(unwrapped, unwrapped_len); os_free(info.payload); os_free(signed_connector); return conf; fail: wpabuf_free(conf); conf = NULL; goto out; } int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len) { const u8 *trans_id, *version, *wrapped_data, *c_nonce, *e_nonce, *reconfig_flags, *status; u16 trans_id_len, version_len, wrapped_data_len, c_nonce_len, e_nonce_len, reconfig_flags_len, status_len; const u8 *addr[2]; size_t len[2]; u8 *unwrapped = NULL; size_t unwrapped_len = 0; int res = -1; u8 flags; if (!auth->reconfig || auth->configurator) goto fail; wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, &wrapped_data_len); if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { dpp_auth_fail(auth, "Missing or invalid required Wrapped Data attribute"); goto fail; } wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", wrapped_data, wrapped_data_len); attr_len = wrapped_data - 4 - attr_start; status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, &status_len); if (!status || status_len < 1) { dpp_auth_fail(auth, "Missing or invalid required DPP Status attribute"); goto fail; } wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); if (status[0] != DPP_STATUS_OK) { dpp_auth_fail(auth, "Reconfiguration did not complete successfully"); goto fail; } addr[0] = hdr; len[0] = DPP_HDR_LEN; addr[1] = attr_start; len[1] = attr_len; wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", wrapped_data, wrapped_data_len); unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; unwrapped = os_malloc(unwrapped_len); if (!unwrapped) goto fail; if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, wrapped_data, wrapped_data_len, 2, addr, len, unwrapped) < 0) { dpp_auth_fail(auth, "AES-SIV decryption failed"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", unwrapped, unwrapped_len); if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); goto fail; } trans_id = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_TRANSACTION_ID, &trans_id_len); if (!trans_id || trans_id_len != 1 || trans_id[0] != auth->transaction_id) { dpp_auth_fail(auth, "Peer did not include valid Transaction ID"); goto fail; } version = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_PROTOCOL_VERSION, &version_len); if (!version || version_len < 1 || version[0] != DPP_VERSION) { dpp_auth_fail(auth, "Missing or invalid Protocol Version attribute"); goto fail; } c_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len); if (!c_nonce || c_nonce_len != auth->curve->nonce_len || os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) { dpp_auth_fail(auth, "Missing or invalid C-nonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len); e_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len); if (!e_nonce || e_nonce_len != auth->curve->nonce_len || os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { dpp_auth_fail(auth, "Missing or invalid E-nonce"); goto fail; } wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len); reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_RECONFIG_FLAGS, &reconfig_flags_len); if (!reconfig_flags || reconfig_flags_len < 1) { dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags"); goto fail; } flags = reconfig_flags[0] & BIT(0); wpa_printf(MSG_DEBUG, "DPP: Reconfig Flags connectorKey=%u", flags); auth->reconfig_connector_key = flags; auth->reconfig_success = true; res = 0; fail: bin_clear_free(unwrapped, unwrapped_len); return res; } #endif /* CONFIG_DPP2 */