diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c index 47174a71b..b6b81dd52 100644 --- a/src/ap/dpp_hostapd.c +++ b/src/ap/dpp_hostapd.c @@ -1958,6 +1958,25 @@ skip_proto_ver: } +static bool hapd_dpp_connector_available(struct hostapd_data *hapd) +{ + if (!hapd->wpa_auth || + !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) || + !(hapd->conf->wpa & WPA_PROTO_RSN)) { + wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use"); + return false; + } + + if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey || + !hapd->conf->dpp_csign) { + wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set"); + return false; + } + + return true; +} + + static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, const u8 *src, const u8 *buf, size_t len, @@ -1971,20 +1990,12 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, int expiration; enum dpp_status_error res; + os_memset(&intro, 0, sizeof(intro)); + wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR, MAC2STR(src)); - if (!hapd->wpa_auth || - !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) || - !(hapd->conf->wpa & WPA_PROTO_RSN)) { - wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use"); + if (!hapd_dpp_connector_available(hapd)) return; - } - - if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey || - !hapd->conf->dpp_csign) { - wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set"); - return; - } os_get_time(&now); @@ -2019,7 +2030,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, wpa_printf(MSG_INFO, "DPP: Network Introduction protocol resulted in internal failure (peer " MACSTR ")", MAC2STR(src)); - return; + goto done; } if (res != DPP_STATUS_OK) { wpa_printf(MSG_INFO, @@ -2027,7 +2038,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, MACSTR " status %d)", MAC2STR(src), res); hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], res); - return; + goto done; } #ifdef CONFIG_DPP3 @@ -2047,7 +2058,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], DPP_STATUS_NO_MATCH); - return; + goto done; } } #endif /* CONFIG_DPP3 */ @@ -2063,11 +2074,13 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, intro.pmkid, expiration, WPA_KEY_MGMT_DPP) < 0) { wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); - return; + goto done; } hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], DPP_STATUS_OK); +done: + dpp_peer_intro_deinit(&intro); } @@ -2599,6 +2612,227 @@ hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd, hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash); } + +static void +hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *trans_id, *version; + u16 trans_id_len, version_len; + struct wpabuf *msg; + u8 ver = DPP_VERSION; + int conn_ver; + + wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from " + MACSTR, MAC2STR(src)); + + if (!hapd_dpp_connector_available(hapd)) + return; + + trans_id = dpp_get_attr(buf, 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"); + return; + } + + version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (!version || version_len != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include Protocol Version"); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u", + trans_id[0], version[0]); + + len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector); + msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len); + if (!msg) + return; + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, trans_id[0]); + + /* Protocol Version */ + conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector); + if (conn_ver > 0 && ver != conn_ver) { + wpa_printf(MSG_DEBUG, + "DPP: Use Connector version %d instead of current protocol version %d", + conn_ver, ver); + ver = conn_ver; + } + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, ver); + + /* DPP Connector */ + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector)); + wpabuf_put_str(msg, hapd->conf->dpp_connector); + + wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to " + MACSTR, MAC2STR(src)); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PRIV_PEER_INTRO_NOTIFY); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void +hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct crypto_ec_key *own_key; + const struct dpp_curve_params *curve; + enum hpke_kem_id kem_id; + enum hpke_kdf_id kdf_id; + enum hpke_aead_id aead_id; + const u8 *aad = hdr; + size_t aad_len = DPP_HDR_LEN; + struct wpabuf *pt; + const u8 *trans_id, *wrapped, *version, *connector; + u16 trans_id_len, wrapped_len, version_len, connector_len; + struct os_time now; + struct dpp_introduction intro; + os_time_t expire; + int expiration; + enum dpp_status_error res; + + os_memset(&intro, 0, sizeof(intro)); + + wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from " + MACSTR, MAC2STR(src)); + + if (!hapd_dpp_connector_available(hapd)) + return; + + os_get_time(&now); + + if (hapd->conf->dpp_netaccesskey_expiry && + (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) { + wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired"); + return; + } + + trans_id = dpp_get_attr(buf, 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"); + return; + } + + wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA, + &wrapped_len); + if (!wrapped) { + wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data"); + return; + } + + own_key = dpp_set_keypair(&curve, + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey)); + if (!own_key) { + wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); + return; + } + + if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) { + wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d", + curve->ike_group); + crypto_ec_key_deinit(own_key); + return; + } + + pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0, + aad, aad_len, wrapped, wrapped_len); + crypto_ec_key_deinit(own_key); + if (!pt) { + wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector"); + return; + } + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt); + + connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt), + DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + goto done; + } + + version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt), + DPP_ATTR_PROTOCOL_VERSION, &version_len); + if (!version || version_len < 1) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include Protocol Version"); + goto done; + } + + res = dpp_peer_intro(&intro, hapd->conf->dpp_connector, + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey), + wpabuf_head(hapd->conf->dpp_csign), + wpabuf_len(hapd->conf->dpp_csign), + connector, connector_len, &expire); + if (res == 255) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in internal failure (peer " + MACSTR ")", MAC2STR(src)); + goto done; + } + if (res != DPP_STATUS_OK) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure (peer " + MACSTR " status %d)", MAC2STR(src), res); + goto done; + } + + if (intro.peer_version && intro.peer_version >= 2) { + u8 attr_version = 1; + + if (version && version_len >= 1) + attr_version = version[0]; + if (attr_version != intro.peer_version) { + wpa_printf(MSG_INFO, + "DPP: Protocol version mismatch (Connector: %d Attribute: %d", + intro.peer_version, attr_version); + goto done; + } + } + + if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire) + expire = hapd->conf->dpp_netaccesskey_expiry; + if (expire) + expiration = expire - now.sec; + else + expiration = 0; + + if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, + intro.pmkid, expiration, + WPA_KEY_MGMT_DPP) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); + goto done; + } + + wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with " + MACSTR, MAC2STR(src)); + +done: + dpp_peer_intro_deinit(&intro); + wpabuf_free(pt); +} + #endif /* CONFIG_DPP3 */ @@ -2712,6 +2946,14 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr, buf, len, freq); break; + case DPP_PA_PRIV_PEER_INTRO_QUERY: + hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr, + buf, len, freq); + break; + case DPP_PA_PRIV_PEER_INTRO_UPDATE: + hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr, + buf, len, freq); + break; #endif /* CONFIG_DPP3 */ default: wpa_printf(MSG_DEBUG, diff --git a/src/common/dpp.c b/src/common/dpp.c index f8532fa9f..433102eb9 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -4100,7 +4100,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, struct json_token *root = NULL, *netkey, *token; struct json_token *own_root = NULL; enum dpp_status_error ret = 255, res; - struct crypto_ec_key *own_key = NULL, *peer_key = NULL; + struct crypto_ec_key *own_key = NULL; struct wpabuf *own_key_pub = NULL; const struct dpp_curve_params *curve, *own_curve; struct dpp_signed_connector_info info; @@ -4173,12 +4173,12 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, goto fail; } - peer_key = dpp_parse_jwk(netkey, &curve); - if (!peer_key) { + intro->peer_key = dpp_parse_jwk(netkey, &curve); + if (!intro->peer_key) { ret = DPP_STATUS_INVALID_CONNECTOR; goto fail; } - dpp_debug_print_key("DPP: Received netAccessKey", peer_key); + dpp_debug_print_key("DPP: Received netAccessKey", intro->peer_key); if (own_curve != curve) { wpa_printf(MSG_DEBUG, @@ -4189,7 +4189,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, } /* ECDH: N = nk * PK */ - if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0) + if (dpp_ecdh(own_key, intro->peer_key, Nx, &Nx_len) < 0) goto fail; wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", @@ -4203,26 +4203,45 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, intro->pmk_len = curve->hash_len; /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ - if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) { + if (dpp_derive_pmkid(curve, own_key, intro->peer_key, intro->pmkid) < + 0) { wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID"); goto fail; } +#ifdef CONFIG_DPP3 + if (dpp_hpke_suite(curve->ike_group, &intro->kem_id, &intro->kdf_id, + &intro->aead_id) < 0) { + wpa_printf(MSG_ERROR, "DPP: Unsupported group %d", + curve->ike_group); + goto fail; + } +#endif /* CONFIG_DPP3 */ + ret = DPP_STATUS_OK; fail: if (ret != DPP_STATUS_OK) - os_memset(intro, 0, sizeof(*intro)); + dpp_peer_intro_deinit(intro); os_memset(Nx, 0, sizeof(Nx)); os_free(info.payload); crypto_ec_key_deinit(own_key); wpabuf_free(own_key_pub); - crypto_ec_key_deinit(peer_key); json_free(root); json_free(own_root); return ret; } +void dpp_peer_intro_deinit(struct dpp_introduction *intro) +{ + if (!intro) + return; + + crypto_ec_key_deinit(intro->peer_key); + os_memset(intro, 0, sizeof(*intro)); +} + + #ifdef CONFIG_DPP3 int dpp_get_connector_version(const char *connector) { diff --git a/src/common/dpp.h b/src/common/dpp.h index bcafcdf0b..da9a33b5e 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -58,6 +58,9 @@ enum dpp_public_action_frame_type { DPP_PA_PKEX_EXCHANGE_REQ = 18, DPP_PA_PB_PRESENCE_ANNOUNCEMENT = 19, DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP = 20, + DPP_PA_PRIV_PEER_INTRO_QUERY = 21, + DPP_PA_PRIV_PEER_INTRO_NOTIFY = 22, + DPP_PA_PRIV_PEER_INTRO_UPDATE = 23, }; enum dpp_attribute_id { @@ -413,6 +416,10 @@ struct dpp_introduction { u8 pmk[PMK_LEN_MAX]; size_t pmk_len; int peer_version; + struct crypto_ec_key *peer_key; + enum hpke_kem_id kem_id; + enum hpke_kdf_id kdf_id; + enum hpke_aead_id aead_id; }; struct dpp_relay_config { @@ -650,6 +657,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, const u8 *csign_key, size_t csign_key_len, const u8 *peer_connector, size_t peer_connector_len, os_time_t *expiry); +void dpp_peer_intro_deinit(struct dpp_introduction *intro); int dpp_get_connector_version(const char *connector); struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, const u8 *own_mac, @@ -687,6 +695,11 @@ struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len); void dpp_pfs_free(struct dpp_pfs *pfs); +struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve, + const u8 *privkey, size_t privkey_len); +int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id, + enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id); + struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name); int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr); diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c index 9c0dd35a8..09d4d8cf0 100644 --- a/src/common/dpp_crypto.c +++ b/src/common/dpp_crypto.c @@ -2367,6 +2367,7 @@ fail: #ifdef CONFIG_DPP3 + int dpp_derive_auth_i(struct dpp_authentication *auth, u8 *auth_i) { int ret = -1, res; @@ -2454,6 +2455,47 @@ fail: wpabuf_free(pex); return ret; } + + +int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id, + enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id) +{ + switch (iana_group) { + case 19: + *kem_id = HPKE_DHKEM_P256_HKDF_SHA256; + *kdf_id = HPKE_KDF_HKDF_SHA256; + *aead_id = HPKE_AEAD_AES_128_GCM; + return 0; + case 20: + *kem_id = HPKE_DHKEM_P384_HKDF_SHA384; + *kdf_id = HPKE_KDF_HKDF_SHA384; + *aead_id = HPKE_AEAD_AES_256_GCM; + return 0; + case 21: + *kem_id = HPKE_DHKEM_P521_HKDF_SHA512; + *kdf_id = HPKE_KDF_HKDF_SHA512; + *aead_id = HPKE_AEAD_AES_256_GCM; + return 0; + case 28: + *kem_id = HPKE_DHKEM_P256_HKDF_SHA256; + *kdf_id = HPKE_KDF_HKDF_SHA256; + *aead_id = HPKE_AEAD_AES_128_GCM; + return 0; + case 29: + *kem_id = HPKE_DHKEM_P384_HKDF_SHA384; + *kdf_id = HPKE_KDF_HKDF_SHA384; + *aead_id = HPKE_AEAD_AES_256_GCM; + return 0; + case 30: + *kem_id = HPKE_DHKEM_P521_HKDF_SHA512; + *kdf_id = HPKE_KDF_HKDF_SHA512; + *aead_id = HPKE_AEAD_AES_256_GCM; + return 0; + } + + return -1; +} + #endif /* CONFIG_DPP3 */ diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h index 051d3fd8e..bdffc9d71 100644 --- a/src/common/dpp_i.h +++ b/src/common/dpp_i.h @@ -98,8 +98,6 @@ int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi, int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi); int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, const u8 *privkey, size_t privkey_len); -struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve, - const u8 *privkey, size_t privkey_len); struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve); int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len); int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len); diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 364bdc905..dcc4e4971 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2630,6 +2630,7 @@ static const struct parse_data ssid_fields[] = { { STR_LEN(dpp_csign) }, { STR_LEN(dpp_pp_key) }, { INT_RANGE(dpp_pfs, 0, 2) }, + { INT_RANGE(dpp_connector_privacy, 0, 1) }, #endif /* CONFIG_DPP */ { INT_RANGE(owe_group, 0, 65535) }, { INT_RANGE(owe_only, 0, 1) }, diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index a779868f3..fcb4c31e7 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -833,6 +833,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(dpp_csign); STR(dpp_pp_key); INT(dpp_pfs); + INT(dpp_connector_privacy); #endif /* CONFIG_DPP */ INT(owe_group); INT(owe_only); diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 767f659c8..f625a0cc3 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -1064,6 +1064,14 @@ struct wpa_ssid { */ int dpp_pfs_fallback; + /** + * dpp_connector_privacy - Network introduction type + * 0: unprotected variant from DPP R1 + * 1: privacy protecting (station Connector encrypted) variant from + * DPP R3 + */ + int dpp_connector_privacy; + /** * owe_group - OWE DH Group * diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 1ed2be03b..70d3136d8 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -3260,6 +3260,274 @@ skip_hash_check: } } + +static void +wpas_dpp_tx_priv_intro_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + const char *res_txt; + + res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED"); + wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR + " result=%s (DPP Private Peer Introduction Update)", + freq, MAC2STR(dst), res_txt); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " freq=%u result=%s", MAC2STR(dst), freq, res_txt); + + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR " version=%u", + MAC2STR(src), wpa_s->dpp_intro_peer_version); + + wpa_printf(MSG_DEBUG, + "DPP: Try connection again after successful network introduction"); + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +} + + +static int +wpas_dpp_send_private_peer_intro_update(struct wpa_supplicant *wpa_s, + struct dpp_introduction *intro, + struct wpa_ssid *ssid, + const u8 *dst, unsigned int freq) +{ + struct wpabuf *pt, *msg, *enc_ct; + size_t len; + u8 ver = DPP_VERSION; + int conn_ver; + const u8 *aad; + size_t aad_len; + unsigned int wait_time; + + wpa_printf(MSG_DEBUG, "HPKE(kem_id=%u kdf_id=%u aead_id=%u)", + intro->kem_id, intro->kdf_id, intro->aead_id); + + /* Plaintext for HPKE */ + len = 5 + 4 + os_strlen(ssid->dpp_connector); + pt = wpabuf_alloc(len); + if (!pt) + return -1; + + /* Protocol Version */ + conn_ver = dpp_get_connector_version(ssid->dpp_connector); + if (conn_ver > 0 && ver != conn_ver) { + wpa_printf(MSG_DEBUG, + "DPP: Use Connector version %d instead of current protocol version %d", + conn_ver, ver); + ver = conn_ver; + } + wpabuf_put_le16(pt, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(pt, 1); + wpabuf_put_u8(pt, ver); + + /* Connector */ + wpabuf_put_le16(pt, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(pt, os_strlen(ssid->dpp_connector)); + wpabuf_put_str(pt, ssid->dpp_connector); + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Plaintext for HPKE", pt); + + /* HPKE(pt) using AP's public key (from its Connector) */ + msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, 0); + if (!msg) { + wpabuf_free(pt); + return -1; + } + aad = wpabuf_head_u8(msg) + 2; /* from the OUI field (inclusive) */ + aad_len = DPP_HDR_LEN; /* to the DPP Frame Type field (inclusive) */ + wpa_hexdump(MSG_MSGDUMP, "DPP: AAD for HPKE", aad, aad_len); + + enc_ct = hpke_base_seal(intro->kem_id, intro->kdf_id, intro->aead_id, + intro->peer_key, NULL, 0, aad, aad_len, + wpabuf_head(pt), wpabuf_len(pt)); + wpabuf_free(pt); + wpabuf_free(msg); + if (!enc_ct) { + wpa_printf(MSG_INFO, "DPP: HPKE Seal(Connector) failed"); + return -1; + } + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE enc|ct", enc_ct); + + /* HPKE(pt) to generate payload for Wrapped Data */ + len = 5 + 4 + wpabuf_len(enc_ct); + msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, len); + if (!msg) { + wpabuf_free(enc_ct); + return -1; + } + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, TRANSACTION_ID); + + /* Wrapped Data */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(enc_ct)); + wpabuf_put_buf(msg, enc_ct); + wpabuf_free(enc_ct); + + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Update", msg); + + /* TODO: Timeout on AP response */ + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(dst), freq, DPP_PA_PRIV_PEER_INTRO_QUERY); + offchannel_send_action(wpa_s, freq, dst, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_priv_intro_status, 0); + wpabuf_free(msg); + + return 0; +} + + +static void +wpas_dpp_rx_priv_peer_intro_notify(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *hdr, + const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpa_ssid *ssid; + const u8 *connector, *trans_id, *version; + u16 connector_len, trans_id_len, version_len; + u8 peer_version = 1; + struct dpp_introduction intro; + struct rsn_pmksa_cache_entry *entry; + struct os_time now; + struct os_reltime rnow; + os_time_t expiry; + unsigned int seconds; + enum dpp_status_error res; + + os_memset(&intro, 0, sizeof(intro)); + + wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Notify from " + MACSTR, MAC2STR(src)); + if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) || + os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from " + MACSTR " - drop", MAC2STR(src)); + return; + } + offchannel_send_action_done(wpa_s); + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->dpp_intro_network) + break; + } + if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey || + !ssid->dpp_csign) { + wpa_printf(MSG_DEBUG, + "DPP: Profile not found for network introduction"); + return; + } + + trans_id = dpp_get_attr(buf, 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"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=missing_transaction_id", MAC2STR(src)); + goto fail; + } + if (trans_id[0] != TRANSACTION_ID) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore frame with unexpected Transaction ID %u", + trans_id[0]); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=transaction_id_mismatch", MAC2STR(src)); + goto fail; + } + + connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=missing_connector", MAC2STR(src)); + goto fail; + } + + version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION, + &version_len); + if (!version || version_len < 1) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include valid Version"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=missing_version", MAC2STR(src)); + goto fail; + } + + res = dpp_peer_intro(&intro, ssid->dpp_connector, + ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len, + ssid->dpp_csign, + ssid->dpp_csign_len, + connector, connector_len, &expiry); + if (res != DPP_STATUS_OK) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=peer_connector_validation_failed", MAC2STR(src)); + wpas_dpp_send_conn_status_result(wpa_s, res); + goto fail; + } + + peer_version = version[0]; + if (intro.peer_version && intro.peer_version >= 2 && + peer_version != intro.peer_version) { + wpa_printf(MSG_INFO, + "DPP: Protocol version mismatch (Connector: %d Attribute: %d", + intro.peer_version, peer_version); + wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_NO_MATCH); + goto fail; + } + wpa_s->dpp_intro_peer_version = peer_version; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + entry->dpp_pfs = peer_version >= 2; + os_memcpy(entry->aa, src, ETH_ALEN); + os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN); + os_memcpy(entry->pmk, intro.pmk, intro.pmk_len); + entry->pmk_len = intro.pmk_len; + entry->akmp = WPA_KEY_MGMT_DPP; + if (expiry) { + os_get_time(&now); + seconds = expiry - now.sec; + } else { + seconds = 86400 * 7; + } + + if (wpas_dpp_send_private_peer_intro_update(wpa_s, &intro, ssid, src, + freq) < 0) { + os_free(entry); + goto fail; + } + + os_get_reltime(&rnow); + entry->expiration = rnow.sec + seconds; + entry->reauth_time = rnow.sec + seconds; + entry->network_ctx = ssid; + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); + + /* Association will be initiated from TX status handler for the Private + * Peer Intro Update: wpas_dpp_tx_priv_intro_status() */ + +fail: + dpp_peer_intro_deinit(&intro); +} + #endif /* CONFIG_DPP3 */ @@ -3377,6 +3645,10 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, wpas_dpp_rx_pb_presence_announcement_resp(wpa_s, src, hdr, buf, len, freq); break; + case DPP_PA_PRIV_PEER_INTRO_NOTIFY: + wpas_dpp_rx_priv_peer_intro_notify(wpa_s, src, hdr, + buf, len, freq); + break; #endif /* CONFIG_DPP3 */ default: wpa_printf(MSG_DEBUG, @@ -3635,6 +3907,63 @@ wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_DPP3 +static int wpas_dpp_start_private_peer_intro(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct wpa_bss *bss) +{ + struct wpabuf *msg; + unsigned int wait_time; + size_t len; + u8 ver = DPP_VERSION; + int conn_ver; + + len = 5 + 5; + msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_QUERY, len); + if (!msg) + return -1; + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, TRANSACTION_ID); + + conn_ver = dpp_get_connector_version(ssid->dpp_connector); + if (conn_ver > 0 && ver != conn_ver) { + wpa_printf(MSG_DEBUG, + "DPP: Use Connector version %d instead of current protocol version %d", + conn_ver, ver); + ver = conn_ver; + } + + /* Protocol Version */ + wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, ver); + + wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Query", msg); + + /* TODO: Timeout on AP response */ + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(bss->bssid), bss->freq, DPP_PA_PRIV_PEER_INTRO_QUERY); + offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_introduction_status, 0); + wpabuf_free(msg); + + /* Request this connection attempt to terminate - new one will be + * started when network introduction protocol completes */ + os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN); + wpa_s->dpp_intro_network = ssid; + return 1; +} +#endif /* CONFIG_DPP3 */ + + int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss) { @@ -3674,11 +4003,18 @@ int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, } wpa_printf(MSG_DEBUG, - "DPP: Starting network introduction protocol to derive PMKSA for " - MACSTR, MAC2STR(bss->bssid)); + "DPP: Starting %snetwork introduction protocol to derive PMKSA for " + MACSTR, + ssid->dpp_connector_privacy ? "private " : "", + MAC2STR(bss->bssid)); if (wpa_s->wpa_state == WPA_SCANNING) wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state); +#ifdef CONFIG_DPP3 + if (ssid->dpp_connector_privacy) + return wpas_dpp_start_private_peer_intro(wpa_s, ssid, bss); +#endif /* CONFIG_DPP3 */ + len = 5 + 4 + os_strlen(ssid->dpp_connector); #ifdef CONFIG_DPP2 len += 5; diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index b5304a77e..90061ba67 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -1493,6 +1493,12 @@ fast_reauth=1 # 2: do not allow PFS to be used #dpp_pfs=0 +# DPP Network introduction type +# 0: unprotected variant from DPP R1 (default) +# 1: privacy protecting (station Connector encrypted) variant from +# DPP R3 +#dpp_connector_privacy=0 + # Whether beacon protection is enabled # This depends on management frame protection (ieee80211w) being enabled and # beacon protection support indication from the driver. diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index a349d6551..e98a34b40 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1459,6 +1459,7 @@ struct wpa_supplicant { int dpp_gas_dialog_token; u8 dpp_intro_bssid[ETH_ALEN]; void *dpp_intro_network; + u8 dpp_intro_peer_version; struct dpp_pkex *dpp_pkex; struct dpp_bootstrap_info *dpp_pkex_bi; char *dpp_pkex_code;