From c6d0e5a93d4aa3185bab3311daf26bcd791dd51b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 6 Aug 2020 19:47:28 +0300 Subject: [PATCH] DPP2: Add E-id in Reconfig Announcement Add an encrypted Enrollee identifier into Reconfig Announcement frames and decrypt that on the Configurator side. The actual E-id value is currently not used for anything, but it can be used in the future to provide better control over reconfiguration. Signed-off-by: Jouni Malinen --- src/ap/dpp_hostapd.c | 10 +- src/common/dpp.h | 15 ++- src/common/dpp_crypto.c | 187 ++++++++++++++++++++++++++++++ src/common/dpp_i.h | 10 ++ src/common/dpp_reconfig.c | 70 ++++++++++- src/common/dpp_tcp.c | 10 +- wpa_supplicant/dpp_supplicant.c | 25 +++- wpa_supplicant/wpa_supplicant_i.h | 1 + 8 files changed, 314 insertions(+), 14 deletions(-) diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index ca225894a..70027ad6e 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -1235,8 +1235,8 @@ hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src, const u8 *hdr, const u8 *buf, size_t len, unsigned int freq) { - const u8 *csign_hash, *fcgroup; - u16 csign_hash_len, fcgroup_len; + const u8 *csign_hash, *fcgroup, *a_nonce, *e_id; + u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len; struct dpp_configurator *conf; struct dpp_authentication *auth; unsigned int wait_time, max_wait_time; @@ -1282,8 +1282,12 @@ hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src, group = WPA_GET_LE16(fcgroup); wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group); + a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len); + e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len); + auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx, - conf, freq, group); + conf, freq, group, a_nonce, a_nonce_len, + e_id, e_id_len); if (!auth) return; hostapd_dpp_set_testing_options(hapd, auth); diff --git a/src/common/dpp.h b/src/common/dpp.h index f6e4e25f2..8243a97b6 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -21,6 +21,7 @@ struct crypto_ecdh; struct hostapd_ip_addr; struct dpp_global; struct json_token; +struct dpp_reconfig_id; #ifdef CONFIG_TESTING_OPTIONS #define DPP_VERSION (dpp_version_override) @@ -87,6 +88,8 @@ enum dpp_attribute_id { DPP_ATTR_RECONFIG_FLAGS = 0x101D, DPP_ATTR_C_SIGN_KEY_HASH = 0x101E, DPP_ATTR_CSR_ATTR_REQ = 0x101F, + DPP_ATTR_A_NONCE = 0x1020, + DPP_ATTR_E_PRIME_ID = 0x1021, }; enum dpp_status_error { @@ -685,10 +688,13 @@ void dpp_global_deinit(struct dpp_global *dpp); 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); + size_t net_access_key_len, + struct dpp_reconfig_id *id); struct dpp_authentication * dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx, - struct dpp_configurator *conf, unsigned int freq, u16 group); + 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 * dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, const char *own_connector, @@ -702,5 +708,10 @@ dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, const u8 *attr_start, size_t attr_len); +struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key, + size_t csign_key_len); +int dpp_update_reconfig_id(struct dpp_reconfig_id *id); +void dpp_free_reconfig_id(struct dpp_reconfig_id *id); + #endif /* CONFIG_DPP */ #endif /* DPP_H */ diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c index 6752aaa31..9dff7c653 100644 --- a/src/common/dpp_crypto.c +++ b/src/common/dpp_crypto.c @@ -2988,6 +2988,193 @@ fail: return ret; } + +struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key, + size_t csign_key_len) +{ + const unsigned char *p; + EVP_PKEY *csign = NULL; + struct dpp_reconfig_id *id = NULL; + BN_CTX *ctx = NULL; + BIGNUM *bn = NULL, *q = NULL; + const EC_KEY *eckey; + const EC_GROUP *group; + EC_POINT *e_id = NULL; + + p = csign_key; + csign = d2i_PUBKEY(NULL, &p, csign_key_len); + if (!csign) + goto fail; + + eckey = EVP_PKEY_get0_EC_KEY(csign); + if (!eckey) + goto fail; + group = EC_KEY_get0_group(eckey); + if (!group) + goto fail; + + e_id = EC_POINT_new(group); + ctx = BN_CTX_new(); + bn = BN_new(); + q = BN_new(); + if (!e_id || !ctx || !bn || !q || + !EC_GROUP_get_order(group, q, ctx) || + !BN_rand_range(bn, q) || + !EC_POINT_mul(group, e_id, bn, NULL, NULL, ctx)) + goto fail; + + dpp_debug_print_point("DPP: Generated random point E-id", group, e_id); + + id = os_zalloc(sizeof(*id)); + if (!id) + goto fail; + id->group = group; + id->e_id = e_id; + e_id = NULL; + id->csign = csign; + csign = NULL; +fail: + EC_POINT_free(e_id); + EVP_PKEY_free(csign); + BN_clear_free(bn); + BN_CTX_free(ctx); + return id; +} + + +static EVP_PKEY * dpp_pkey_from_point(const EC_GROUP *group, + const EC_POINT *point) +{ + EC_KEY *eckey; + EVP_PKEY *pkey = NULL; + + eckey = EC_KEY_new(); + if (!eckey || + EC_KEY_set_group(eckey, group) != 1 || + EC_KEY_set_public_key(eckey, point) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to set EC_KEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); + EVP_PKEY_free(pkey); + pkey = NULL; + goto fail; + } + +fail: + EC_KEY_free(eckey); + return pkey; +} + + +int dpp_update_reconfig_id(struct dpp_reconfig_id *id) +{ + BN_CTX *ctx = NULL; + BIGNUM *bn = NULL, *q = NULL; + EC_POINT *e_prime_id = NULL, *a_nonce = NULL; + int ret = -1; + const EC_KEY *csign; + const EC_POINT *csign_point; + + csign = EVP_PKEY_get0_EC_KEY(id->csign); + if (!csign) + goto fail; + csign_point = EC_KEY_get0_public_key(csign); + e_prime_id = EC_POINT_new(id->group); + a_nonce = EC_POINT_new(id->group); + ctx = BN_CTX_new(); + bn = BN_new(); + q = BN_new(); + /* Generate random 0 <= a-nonce < q + * A-NONCE = a-nonce * G + * E'-id = E-id + a-nonce * S_C */ + if (!csign_point || !e_prime_id || !a_nonce || !ctx || !bn || !q || + !EC_GROUP_get_order(id->group, q, ctx) || + !BN_rand_range(bn, q) || /* bn = a-nonce */ + !EC_POINT_mul(id->group, a_nonce, bn, NULL, NULL, ctx) || + !EC_POINT_mul(id->group, e_prime_id, NULL, csign_point, bn, ctx) || + !EC_POINT_add(id->group, e_prime_id, id->e_id, e_prime_id, ctx)) + goto fail; + + dpp_debug_print_point("DPP: Generated A-NONCE", id->group, a_nonce); + dpp_debug_print_point("DPP: Encrypted E-id to E'-id", + id->group, e_prime_id); + + EVP_PKEY_free(id->a_nonce); + EVP_PKEY_free(id->e_prime_id); + id->a_nonce = dpp_pkey_from_point(id->group, a_nonce); + id->e_prime_id = dpp_pkey_from_point(id->group, e_prime_id); + if (!id->a_nonce || !id->e_prime_id) + goto fail; + + ret = 0; + +fail: + EC_POINT_free(e_prime_id); + EC_POINT_free(a_nonce); + BN_clear_free(bn); + BN_CTX_free(ctx); + return ret; +} + + +void dpp_free_reconfig_id(struct dpp_reconfig_id *id) +{ + if (id) { + EC_POINT_clear_free(id->e_id); + EVP_PKEY_free(id->csign); + EVP_PKEY_free(id->a_nonce); + EVP_PKEY_free(id->e_prime_id); + os_free(id); + } +} + + +EC_POINT * dpp_decrypt_e_id(EVP_PKEY *csign, EVP_PKEY *a_nonce, + EVP_PKEY *e_prime_id) +{ + const EC_KEY *csign_ec, *a_nonce_ec, *e_prime_id_ec; + const BIGNUM *csign_bn; + const EC_GROUP *group; + EC_POINT *e_id = NULL; + const EC_POINT *a_nonce_point, *e_prime_id_point; + BN_CTX *ctx = NULL; + + /* E-id = E'-id - s_C * A-NONCE */ + csign_ec = EVP_PKEY_get0_EC_KEY(csign); + a_nonce_ec = EVP_PKEY_get0_EC_KEY(a_nonce); + e_prime_id_ec = EVP_PKEY_get0_EC_KEY(e_prime_id); + if (!csign_ec || !a_nonce_ec || !e_prime_id_ec) + return NULL; + csign_bn = EC_KEY_get0_private_key(csign_ec); + group = EC_KEY_get0_group(csign_ec); + a_nonce_point = EC_KEY_get0_public_key(a_nonce_ec); + e_prime_id_point = EC_KEY_get0_public_key(e_prime_id_ec); + ctx = BN_CTX_new(); + if (!csign_bn || !group || !a_nonce_point || !e_prime_id_point || !ctx) + goto fail; + e_id = EC_POINT_new(group); + if (!e_id || + !EC_POINT_mul(group, e_id, NULL, a_nonce_point, csign_bn, ctx) || + !EC_POINT_invert(group, e_id, ctx) || + !EC_POINT_add(group, e_id, e_prime_id_point, e_id, ctx)) { + EC_POINT_clear_free(e_id); + goto fail; + } + + dpp_debug_print_point("DPP: Decrypted E-id", group, e_id); + +fail: + BN_CTX_free(ctx); + return e_id; +} + #endif /* CONFIG_DPP2 */ diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h index cf3dedd01..f2164c789 100644 --- a/src/common/dpp_i.h +++ b/src/common/dpp_i.h @@ -133,11 +133,21 @@ int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth, int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth, const u8 *r_proto, u16 r_proto_len, struct json_token *net_access_key); +EC_POINT * dpp_decrypt_e_id(EVP_PKEY *csign, EVP_PKEY *a_nonce, + EVP_PKEY *e_prime_id); char * dpp_sign_connector(struct dpp_configurator *conf, const struct wpabuf *dppcon); int dpp_test_gen_invalid_key(struct wpabuf *msg, const struct dpp_curve_params *curve); +struct dpp_reconfig_id { + const EC_GROUP *group; + EC_POINT *e_id; /* E-id */ + EVP_PKEY *csign; + EVP_PKEY *a_nonce; /* A-NONCE */ + EVP_PKEY *e_prime_id; /* E'-id */ +}; + /* dpp_tcp.c */ void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx, diff --git a/src/common/dpp_reconfig.c b/src/common/dpp_reconfig.c index 5d7ab6802..b12c4f7d0 100644 --- a/src/common/dpp_reconfig.c +++ b/src/common/dpp_reconfig.c @@ -36,7 +36,8 @@ static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash) 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) + size_t net_access_key_len, + struct dpp_reconfig_id *id) { struct wpabuf *msg = NULL; EVP_PKEY *csign = NULL; @@ -49,6 +50,7 @@ struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key, size_t attr_len; const struct dpp_curve_params *own_curve; EVP_PKEY *own_key; + struct wpabuf *a_nonce = NULL, *e_id = NULL; wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame"); @@ -81,8 +83,20 @@ struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key, 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 = dpp_get_pubkey_point(id->a_nonce, 0); + e_id = dpp_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; @@ -97,9 +111,21 @@ struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key, 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); EVP_PKEY_free(own_key); return msg; } @@ -198,10 +224,14 @@ fail: struct dpp_authentication * dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx, - struct dpp_configurator *conf, unsigned int freq, u16 group) + 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; + EVP_PKEY *a_nonce, *e_prime_id; + EC_POINT *e_id; curve = dpp_get_curve_ike_group(group); if (!curve) { @@ -211,6 +241,42 @@ dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx, 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"); + EVP_PKEY_free(a_nonce); + return NULL; + } + dpp_debug_print_key("E'-id", e_prime_id); + e_id = dpp_decrypt_e_id(conf->csign, a_nonce, e_prime_id); + EVP_PKEY_free(a_nonce); + EVP_PKEY_free(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 */ + EC_POINT_clear_free(e_id); + auth = dpp_alloc_auth(dpp, msg_ctx); if (!auth) return NULL; diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c index a4ec81398..639ff8c9f 100644 --- a/src/common/dpp_tcp.c +++ b/src/common/dpp_tcp.c @@ -844,8 +844,8 @@ static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn, const u8 *hdr, const u8 *buf, size_t len) { - const u8 *csign_hash, *fcgroup; - u16 csign_hash_len, fcgroup_len; + const u8 *csign_hash, *fcgroup, *a_nonce, *e_id; + u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len; struct dpp_configurator *conf; struct dpp_global *dpp = conn->ctrl->global; struct dpp_authentication *auth; @@ -885,7 +885,11 @@ static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn, group = WPA_GET_LE16(fcgroup); wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group); - auth = dpp_reconfig_init(dpp, dpp->msg_ctx, conf, 0, group); + a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len); + e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len); + + auth = dpp_reconfig_init(dpp, dpp->msg_ctx, conf, 0, group, + a_nonce, a_nonce_len, e_id, e_id_len); if (!auth) return -1; if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) { diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 9c0056c5b..1184591e4 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -2022,8 +2022,8 @@ wpas_dpp_rx_reconfig_announcement(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *hdr, const u8 *buf, size_t len, unsigned int freq) { - const u8 *csign_hash, *fcgroup; - u16 csign_hash_len, fcgroup_len; + const u8 *csign_hash, *fcgroup, *a_nonce, *e_id; + u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len; struct dpp_configurator *conf; struct dpp_authentication *auth; unsigned int wait_time, max_wait_time; @@ -2067,7 +2067,11 @@ wpas_dpp_rx_reconfig_announcement(struct wpa_supplicant *wpa_s, const u8 *src, group = WPA_GET_LE16(fcgroup); wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group); - auth = dpp_reconfig_init(wpa_s->dpp, wpa_s, conf, freq, group); + a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len); + e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len); + + auth = dpp_reconfig_init(wpa_s->dpp, wpa_s, conf, freq, group, + a_nonce, a_nonce_len, e_id, e_id_len); if (!auth) return; wpas_dpp_set_testing_options(wpa_s, auth); @@ -3303,6 +3307,8 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) dpp_pfs_free(wpa_s->dpp_pfs); wpa_s->dpp_pfs = NULL; wpas_dpp_chirp_stop(wpa_s); + dpp_free_reconfig_id(wpa_s->dpp_reconfig_id); + wpa_s->dpp_reconfig_id = NULL; #endif /* CONFIG_DPP2 */ offchannel_send_action_done(wpa_s); wpas_dpp_listen_stop(wpa_s); @@ -3642,14 +3648,25 @@ int wpas_dpp_reconfig(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) return -1; } + dpp_free_reconfig_id(wpa_s->dpp_reconfig_id); + wpa_s->dpp_reconfig_id = dpp_gen_reconfig_id(ssid->dpp_csign, + ssid->dpp_csign_len); + if (!wpa_s->dpp_reconfig_id) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to generate E-id for reconfiguration"); + return -1; + } wpas_dpp_chirp_stop(wpa_s); wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; wpa_s->dpp_qr_mutual = 0; + /* TODO: regenerate Reconfig Announcement frame to update A-NONCE/E'-id + * for each retransmission */ wpa_s->dpp_reconfig_announcement = dpp_build_reconfig_announcement(ssid->dpp_csign, ssid->dpp_csign_len, ssid->dpp_netaccesskey, - ssid->dpp_netaccesskey_len); + ssid->dpp_netaccesskey_len, + wpa_s->dpp_reconfig_id); if (!wpa_s->dpp_reconfig_announcement) return -1; wpa_s->dpp_reconfig_ssid = ssid; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 6dff477bf..d3a3da6e1 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1296,6 +1296,7 @@ struct wpa_supplicant { int dpp_chirp_listen; struct wpa_ssid *dpp_reconfig_ssid; int dpp_reconfig_ssid_id; + struct dpp_reconfig_id *dpp_reconfig_id; #endif /* CONFIG_DPP2 */ #ifdef CONFIG_TESTING_OPTIONS char *dpp_config_obj_override;