[PATCH] DPP: expose enrollee pubkey hash for identification

Michal Kazior kazikcz at gmail.com
Mon Mar 29 15:03:11 BST 2021


From: Michal Kazior <michal at plume.com>

Just like with WPA-PSK and keyids it may be
desired to identify connecting clients to provide
additional network filtering.

This does:

 - adds a new control event generated during DPP
   Auth so that the AP side can learn what the
   resulting public key hash is

 - store public key hash in PMKSA from DPP Network
   Intro for later use

 - extend sta mib to print out the stored
   dpp_pkhash from PMKSA if present

 - extend AP_STA_CONNECTED to print out the stored
   dpp_pkhash from PMKSA if present

Signed-off-by: Michal Kazior <michal at plume.com>
---
Open question:

I'm not sure if it's really necessary to salt the
public key hash for this purpose. Chirping does it
because it's exposing public key which can be used
to initiate DPP Auth. Control interface is local
to a system and this is the ephemeral key derived
from DPP Auth itself, so it can't be abused like
the other public key can.

Thoughts?


 src/ap/ctrl_iface_ap.c          |  8 +++++++
 src/ap/dpp_hostapd.c            | 10 ++++++---
 src/ap/pmksa_cache_auth.c       |  1 +
 src/ap/pmksa_cache_auth.h       |  1 +
 src/ap/sta_info.c               | 24 +++++++++++++++++----
 src/ap/sta_info.h               |  2 ++
 src/ap/wpa_auth.c               | 33 ++++++++++++++++++++++++++++
 src/ap/wpa_auth.h               |  4 ++++
 src/common/dpp.c                |  8 +++++--
 src/common/dpp.h                |  2 +-
 src/common/dpp_auth.c           |  5 +++++
 src/common/dpp_crypto.c         | 38 +++++++++++++++++++++++++++++++++
 src/common/dpp_i.h              |  1 +
 src/common/wpa_ctrl.h           |  1 +
 wpa_supplicant/dpp_supplicant.c |  2 +-
 15 files changed, 129 insertions(+), 11 deletions(-)

diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 28e40ba9c..e663a60cb 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -208,6 +208,7 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
 {
 	int len, res, ret, i;
 	const char *keyid;
+	const char *dpp_pkhash;
 
 	if (!sta)
 		return 0;
@@ -377,6 +378,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
 			len += ret;
 	}
 
+	dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+	if (dpp_pkhash) {
+		ret = os_snprintf(buf + len, buflen - len, "dpp_pkhash=%s\n", dpp_pkhash);
+		if (!os_snprintf_error(buflen - len, ret))
+			len += ret;
+	}
+
 	return len;
 }
 
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index aaeb94c2f..955df86ea 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -1588,6 +1588,8 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
 	os_time_t expire;
 	int expiration;
 	enum dpp_status_error res;
+	EVP_PKEY *peer_key = NULL;
+	char hex[SHA256_MAC_LEN*2 + 1];
 
 	wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
 		   MAC2STR(src));
@@ -1632,7 +1634,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
 			     wpabuf_len(hapd->conf->dpp_netaccesskey),
 			     wpabuf_head(hapd->conf->dpp_csign),
 			     wpabuf_len(hapd->conf->dpp_csign),
-			     connector, connector_len, &expire);
+			     connector, connector_len, &expire, &peer_key);
 	if (res == 255) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in internal failure (peer "
@@ -1655,9 +1657,11 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
 	else
 		expiration = 0;
 
-	if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+	dpp_get_pubkey_hash(peer_key, hex, sizeof(hex), "hostap");
+
+	if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
 				intro.pmkid, expiration,
-				WPA_KEY_MGMT_DPP) < 0) {
+				WPA_KEY_MGMT_DPP, hex) < 0) {
 		wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
 		return;
 	}
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index fe5f81717..29d2b500a 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -40,6 +40,7 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
 {
 	os_free(entry->vlan_desc);
 	os_free(entry->identity);
+	os_free(entry->dpp_pkhash);
 	wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
 	radius_free_class(&entry->radius_class);
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 2ef217435..c80c0e720 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -23,6 +23,7 @@ struct rsn_pmksa_cache_entry {
 	int akmp; /* WPA_KEY_MGMT_* */
 	u8 spa[ETH_ALEN];
 
+	u8 *dpp_pkhash;
 	u8 *identity;
 	size_t identity_len;
 	struct wpabuf *cui;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index ccd1ed931..9a80efd4d 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1260,6 +1260,13 @@ const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
 }
 
 
+const char * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				       struct sta_info *sta)
+{
+	return wpa_auth_get_dpp_pkhash(sta->wpa_sm);
+}
+
+
 void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 			   int authorized)
 {
@@ -1298,10 +1305,13 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 					sta->addr, authorized, dev_addr);
 
 	if (authorized) {
+		const char *dpp_pkhash;
 		const char *keyid;
+		char dpp_pkhash_buf[100];
 		char keyid_buf[100];
 		char ip_addr[100];
 
+		dpp_pkhash_buf[0] = '\0';
 		keyid_buf[0] = '\0';
 		ip_addr[0] = '\0';
 #ifdef CONFIG_P2P
@@ -1319,14 +1329,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 				    " keyid=%s", keyid);
 		}
 
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
-			buf, ip_addr, keyid_buf);
+		dpp_pkhash = ap_sta_wpa_get_dpp_pkhash(hapd, sta);
+		if (dpp_pkhash) {
+			os_snprintf(dpp_pkhash_buf, sizeof(dpp_pkhash_buf),
+				    " dpp_pkhash=%s", dpp_pkhash);
+		}
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s%s",
+			buf, ip_addr, keyid_buf, dpp_pkhash_buf);
 
 		if (hapd->msg_ctx_parent &&
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-					  AP_STA_CONNECTED "%s%s%s",
-					  buf, ip_addr, keyid_buf);
+					  AP_STA_CONNECTED "%s%s%s%s",
+					  buf, ip_addr, keyid_buf, dpp_pkhash_buf);
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index efa48e7e3..7f9ef3cc4 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -384,6 +384,8 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
 const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
 				  struct sta_info *sta);
+const char * ap_sta_wpa_get_dpp_pkhash(struct hostapd_data *hapd,
+				       struct sta_info *sta);
 void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *addr, u16 reason);
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index ef0595c57..d5b446abb 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -4683,6 +4683,16 @@ const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
 }
 
 
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm)
+{
+	if (!sm)
+		return NULL;
+	if (!sm->pmksa)
+		return NULL;
+	return sm->pmksa->dpp_pkhash;
+}
+
+
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
 {
 	if (!sm)
@@ -4844,6 +4854,29 @@ int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 }
 
 
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp, const u8 *dpp_pkhash)
+{
+	struct rsn_pmksa_cache_entry *entry;
+
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
+	entry = pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+				 NULL, 0, wpa_auth->addr, addr, session_timeout,
+				 NULL, akmp);
+	if (!entry)
+		return -1;
+
+	if (dpp_pkhash)
+		entry->dpp_pkhash = os_strdup(dpp_pkhash);
+
+	return 0;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr)
 {
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index eaa2cafc8..b9fd005f7 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -401,6 +401,7 @@ void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
 int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
 int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
 const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
+const u8 * wpa_auth_get_dpp_pkhash(struct wpa_state_machine *sm);
 int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
@@ -425,6 +426,9 @@ void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
 int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
 			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
 			int session_timeout, int akmp);
+int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			int session_timeout, int akmp, const u8 *dpp_pkhash);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
diff --git a/src/common/dpp.c b/src/common/dpp.c
index 3c8c7682d..e32580212 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -3665,7 +3665,8 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
 	       const u8 *net_access_key, size_t net_access_key_len,
 	       const u8 *csign_key, size_t csign_key_len,
 	       const u8 *peer_connector, size_t peer_connector_len,
-	       os_time_t *expiry)
+	       os_time_t *expiry,
+	       EVP_PKEY **peer_key_out)
 {
 	struct json_token *root = NULL, *netkey, *token;
 	struct json_token *own_root = NULL;
@@ -3778,7 +3779,10 @@ fail:
 	os_free(info.payload);
 	EVP_PKEY_free(own_key);
 	wpabuf_free(own_key_pub);
-	EVP_PKEY_free(peer_key);
+	if (!peer_key_out)
+		EVP_PKEY_free(peer_key);
+	else
+		*peer_key_out = peer_key;
 	json_free(root);
 	json_free(own_root);
 	return ret;
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 65ee905a7..98aa8cbf9 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -594,7 +594,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
 	       const u8 *net_access_key, size_t net_access_key_len,
 	       const u8 *csign_key, size_t csign_key_len,
 	       const u8 *peer_connector, size_t peer_connector_len,
-	       os_time_t *expiry);
+	       os_time_t *expiry, EVP_PKEY **peer_key_out);
 struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
 				const u8 *own_mac,
 				const char *identifier,
diff --git a/src/common/dpp_auth.c b/src/common/dpp_auth.c
index 0cabd647f..b43bdf04f 100644
--- a/src/common/dpp_auth.c
+++ b/src/common/dpp_auth.c
@@ -1408,6 +1408,7 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
 	EVP_PKEY *pr;
 	size_t secret_len;
 	const u8 *addr[2];
+	u8 hex[SHA256_MAC_LEN*2 + 1];
 	size_t len[2];
 	u8 *unwrapped = NULL, *unwrapped2 = NULL;
 	size_t unwrapped_len = 0, unwrapped2_len = 0;
@@ -1563,6 +1564,10 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
 	}
 	dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
 
+	os_memset(hex, 0, sizeof(hex));
+	dpp_get_pubkey_hash(pr, hex, sizeof(hex), "hostap");
+	wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_PK_HASH "%s", hex);
+
 	if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
 		dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
 		goto fail;
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index c75fc7871..1f2c3fc0a 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -520,6 +520,44 @@ EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len)
 }
 
 
+int dpp_get_pubkey_hash(EVP_PKEY *key, u8 *hexstr, size_t len, const char *salt)
+{
+	unsigned char *der = NULL;
+	const u8 *args[2];
+	size_t lens[2];
+	char buf[SHA256_MAC_LEN];
+	EC_KEY *eckey;
+	int der_len;
+	int res = 0;
+
+	os_memset(buf, 0, sizeof(buf));
+
+	eckey = EVP_PKEY_get1_EC_KEY(key);
+	if (!eckey)
+		return -1;
+
+	if (!salt)
+		salt = "";
+
+	der_len = i2d_EC_PUBKEY(eckey, &der);
+	if (der_len > 0) {
+		args[0] = salt;
+		lens[0] = strlen(salt);
+		args[1] = der;
+		lens[1] = der_len;
+
+		if (sha256_vector(2, args, lens, buf) < 0)
+			res = -1;
+
+	}
+	OPENSSL_free(der);
+	EC_KEY_free(eckey);
+
+	wpa_snprintf_hex(hexstr, len, buf, sizeof(buf));
+	return res;
+}
+
+
 EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
 {
 	EVP_PKEY_CTX *kctx = NULL;
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index af12467a5..418cf3fab 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -81,6 +81,7 @@ EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
 				      const u8 *buf_x, const u8 *buf_y,
 				      size_t len);
 EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len);
+int dpp_get_pubkey_hash(EVP_PKEY *key, u8 *hexstr, size_t len, const char *salt);
 int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len);
 int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
 		    const char *label, u8 *out, size_t outlen);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 126a7892c..58a554225 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -173,6 +173,7 @@ extern "C" {
 #define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
 #define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
 #define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION "
+#define DPP_EVENT_AUTH_PK_HASH "DPP-AUTH-PK-HASH "
 #define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
 #define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
 #define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c
index 2bcf10b4e..70367b95b 100644
--- a/wpa_supplicant/dpp_supplicant.c
+++ b/wpa_supplicant/dpp_supplicant.c
@@ -2437,7 +2437,7 @@ static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s,
 			     ssid->dpp_netaccesskey_len,
 			     ssid->dpp_csign,
 			     ssid->dpp_csign_len,
-			     connector, connector_len, &expiry);
+			     connector, connector_len, &expiry, NULL);
 	if (res != DPP_STATUS_OK) {
 		wpa_printf(MSG_INFO,
 			   "DPP: Network Introduction protocol resulted in failure");
-- 
2.27.0




More information about the Hostap mailing list