| /* |
| * 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 */ |