From 601faef85b4dc1fee9cdc963aacd213f442a97be Mon Sep 17 00:00:00 2001 From: Akshat Agrawal Date: Fri, 12 Jun 2026 18:12:20 +0530 Subject: [PATCH] fix(nan): Add service hash to NVS to maintain pairing states after reset --- .../esp_wifi/wifi_apps/nan_app/src/nan_app.c | 46 +++++++++++++++++-- .../esp_wifi/wifi_apps/nan_app/src/nan_i.h | 3 ++ .../wifi_apps/nan_app/src/nan_pairing.c | 22 +++++---- .../wifi_apps/nan_app/src/nan_security.c | 2 +- .../esp_supplicant/src/esp_wifi_driver.h | 3 +- 5 files changed, 61 insertions(+), 15 deletions(-) diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c index 76879988a70..a1336e65cbb 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c @@ -479,6 +479,7 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_ p_svc->type = type; strlcpy(p_svc->svc_name, svc_name, ESP_WIFI_MAX_SVC_NAME_LEN); SLIST_INIT(&p_svc->peer_list); + #ifdef CONFIG_ESP_WIFI_NAN_SECURITY forced_memzero(&p_svc->user_cfg, sizeof(p_svc->user_cfg)); forced_memzero(&p_svc->derived_security, sizeof(p_svc->derived_security)); @@ -499,13 +500,14 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_ /* Stamp the real svc_id and per-publish flags after the blob accepts the * service. Looked up by name since the WiFi-task derive callback may have * already populated derived_security[] before this runs. */ -static void nan_finalize_own_svc(const char *svc_name, uint8_t id, bool ndp_resp_needed) +static void nan_finalize_own_svc(const char *svc_name, uint8_t id, bool ndp_resp_needed, uint8_t service_hash[6]) { struct own_svc_info *p_svc = nan_find_own_svc_by_name(svc_name); if (!p_svc) { return; } p_svc->svc_id = id; + memcpy(p_svc->svc_hash, service_hash, 6); if (p_svc->type == ESP_NAN_PUBLISH) { p_svc->ndp_resp_needed = ndp_resp_needed; } @@ -1624,9 +1626,23 @@ esp_err_t esp_wifi_nan_sync_stop(void) } #endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */ +#ifdef CONFIG_ESP_WIFI_NAN_PAIRING +static bool nan_check_paired_service_hash(uint8_t service_hash[6]) +{ + for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) { + if (s_nan_ctx.peer_creds[i].is_valid && + os_memcmp(s_nan_ctx.peer_creds[i].service_hash, service_hash, 6) == 0) { + return true; + } + } + return false; +} +#endif + uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg) { int pub_id = 0; + uint8_t service_id[6] = {0}; if (publish_cfg->usd_discovery_flag && !s_usd_in_progress) { ESP_LOGE(TAG, "Can not start Publish function with USD Discovery " @@ -1758,6 +1774,17 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg) * into p_svc->derived_security[]. Doing the derive on WiFi task (not the * app/main task) keeps PBKDF2's hardware-SHA polling off IDLE0 and avoids * tripping the task watchdog when num_credentials > 1. */ + if (!nan_compute_service_id(publish_cfg->service_name, service_id)) { + ESP_LOGE(TAG, "Failed to compute Service ID for %s", publish_cfg->service_name); + goto fail; + } + +#ifdef CONFIG_ESP_WIFI_NAN_PAIRING + if (nan_check_paired_service_hash(service_id)) { + cfg->pairing->pairing_setup = false; + } +#endif + if (!nan_claim_own_svc_slot(ESP_NAN_PUBLISH, publish_cfg->service_name, #ifdef CONFIG_ESP_WIFI_NAN_SECURITY cfg->security_cfg, @@ -1781,7 +1808,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg) } ESP_LOGI(TAG, "Started Publishing %s [Service ID - %u]", publish_cfg->service_name, pub_id); - nan_finalize_own_svc(publish_cfg->service_name, pub_id, publish_cfg->ndp_resp_needed); + nan_finalize_own_svc(publish_cfg->service_name, pub_id, publish_cfg->ndp_resp_needed, service_id); if (cfg->pairing) { os_free(cfg->pairing); } @@ -1805,6 +1832,7 @@ fail: uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg) { int sub_id = 0; + uint8_t service_id[6] = {0}; if (subscribe_cfg->usd_discovery_flag && !s_usd_in_progress) { ESP_LOGE(TAG, "Can not start Subscribe function with USD Discovery " @@ -1905,6 +1933,18 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe /* Pre-claim host slot BEFORE the blob's subscribe call; see comment on * the publish path for the watchdog rationale. */ + + if (!nan_compute_service_id(subscribe_cfg->service_name, service_id)) { + ESP_LOGE(TAG, "Failed to compute Service ID for %s", subscribe_cfg->service_name); + goto fail; + } + +#ifdef CONFIG_ESP_WIFI_NAN_PAIRING + if (nan_check_paired_service_hash(service_id)) { + subscribe_cfg->pairing->pairing_setup = false; + } +#endif + if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name, subscribe_cfg->security_cfg, subscribe_cfg->pairing)) { ESP_LOGE(TAG, "No free service slot"); @@ -1918,7 +1958,7 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe } ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", subscribe_cfg->service_name, sub_id); - nan_finalize_own_svc(subscribe_cfg->service_name, (uint8_t) sub_id, false); + nan_finalize_own_svc(subscribe_cfg->service_name, (uint8_t) sub_id, false, service_id); NAN_DATA_UNLOCK(); return sub_id; diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h index a46d5860ec3..d343c126aff 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h @@ -232,6 +232,7 @@ struct own_svc_info { bool nik_fup_pending; uint8_t nik_fup_pending_peer_nmi[MACADDR_LEN]; #endif + uint8_t svc_hash[6]; }; /* Per-NDP link state */ @@ -486,6 +487,8 @@ esp_err_t nan_app_register_paired_peer(const uint8_t *peer_nmi, const struct nan_paired_peer *nan_app_find_paired_peer(const uint8_t *peer_nmi); void nan_app_remove_paired_peer(const uint8_t *peer_nmi); void nan_app_clear_paired_peers(void); +bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]); + #endif /* CONFIG_ESP_WIFI_NAN_SECURITY */ #ifdef __cplusplus diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c index 4a4ac938adb..c24554c0901 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c @@ -868,7 +868,7 @@ static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi, * for NIRA identity resolution. Caller holds NAN_DATA_LOCK. A @a npk of NULL * stores a zeroed key. When the cache is full the oldest entry (slot 0) is * reused. */ -static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *npk) +static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *npk, uint8_t service_hash[6]) { wifi_nan_peer_creds_t *slot = NULL; @@ -889,6 +889,7 @@ static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *np } memcpy(slot->peer_nik, peer_nik, ESP_WIFI_NAN_NIK_LEN); + memcpy(slot->service_hash, service_hash, 6); if (npk) { memcpy(slot->npk, npk, ESP_WIFI_NAN_NPK_LEN); } else { @@ -940,9 +941,9 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id, bool already_had_nik = false; bool pairing_completed = false; - uint8_t own_svc_id_to_disable = 0; bool persist_creds = false; uint8_t persist_npk[ESP_WIFI_NAN_NPK_LEN] = {0}; + struct own_svc_info *own = NULL; NAN_DATA_LOCK(); struct peer_svc_info *p_peer_svc = nan_find_peer_svc_exact(svc_id, peer_svc_id, peer_mac); @@ -961,33 +962,33 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id, if (paired) { memcpy(persist_npk, paired->nd_pmk, ESP_WIFI_NAN_NPK_LEN); } - nan_app_update_peer_creds(nik, paired ? paired->nd_pmk : NULL); - persist_creds = s_nan_ctx.use_nvs_for_caching; + + own = nan_find_own_svc(p_peer_svc->own_svc_id); if (!already_had_nik) { pairing_completed = true; - own_svc_id_to_disable = p_peer_svc->own_svc_id; } + + nan_app_update_peer_creds(nik, paired ? paired->nd_pmk : NULL, own ? own->svc_hash : NULL); + persist_creds = s_nan_ctx.use_nvs_for_caching; } NAN_DATA_UNLOCK(); /* Persist outside the lock; NVS writes can block. */ if (persist_creds) { - esp_wifi_nan_save_creds_for_peer(nik, persist_npk); + esp_wifi_nan_save_creds_for_peer(nik, persist_npk, own ? own->svc_hash : NULL); } - /* Invoke blocking calls outside NAN_DATA_LOCK to avoid deadlock. */ if (pairing_completed) { wifi_event_nan_pairing_complete_t evt = {0}; - struct own_svc_info *own = nan_find_own_svc(own_svc_id_to_disable); if (own) { nan_pairing_cancel_svc_pending(own); } evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED; evt.reason_code = 0; MACADDR_COPY(evt.peer_nmi, peer_mac); - esp_nan_complete_pairing(own_svc_id_to_disable); + esp_nan_complete_pairing(p_peer_svc ? p_peer_svc->own_svc_id : 0); nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt)); } @@ -1047,6 +1048,7 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at if (!s_nan_ctx.peer_creds[i].is_valid) { continue; } + if (nan_pairing_derive_nira_tag(s_nan_ctx.peer_creds[i].peer_nik, peer_mac, nonce, expected_tag) != 0) { continue; @@ -1061,7 +1063,7 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at if (match) { ESP_LOGD(TAG, "NIRA verify: OK for "MACSTR, MAC2STR(peer_mac)); } else { - ESP_LOGW(TAG, "NIRA verify: no matching NIK for "MACSTR, MAC2STR(peer_mac)); + ESP_LOGD(TAG, "NIRA verify: no matching NIK for "MACSTR, MAC2STR(peer_mac)); } return match; } diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c index fa408b91742..5fbf2b8227c 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c @@ -103,7 +103,7 @@ static bool nan_csid_bitmap_has_pasn(uint16_t csid_bitmap) * Service ID = first 6 bytes of SHA256(lowercase(service_name)) * per Wi-Fi Aware v4.0 ยง5.1.5 (Service Name and Service ID). */ -static bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]) +bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]) { if (!service_name || !g_wifi_default_wpa_crypto_funcs.sha256_vector) { return false; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h index 774e25f094d..a71272dcc4c 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h @@ -243,6 +243,7 @@ typedef enum { typedef struct { uint8_t peer_nik[ESP_WIFI_NAN_NIK_LEN]; /**< Peer's NAN Identity Key (16 bytes) */ uint8_t npk[ESP_WIFI_NAN_NPK_LEN]; /**< NAN Pairwise Key / NCS-SK PMK (32 bytes) */ + uint8_t service_hash[6]; bool is_valid; } wifi_nan_peer_creds_t; @@ -345,7 +346,7 @@ esp_err_t esp_wifi_nan_load_saved_creds(uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN], b wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS], uint8_t *num_peer_creds); esp_err_t esp_wifi_nan_save_own_nik(const uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN]) ; esp_err_t esp_wifi_nan_save_creds_for_peer(const uint8_t peer_nik[ESP_WIFI_NAN_NIK_LEN], - const uint8_t npk[ESP_WIFI_NAN_NPK_LEN]); + const uint8_t npk[ESP_WIFI_NAN_NPK_LEN], const uint8_t service_hash[6]); esp_err_t esp_wifi_nan_erase_all_creds(void); #endif /* _ESP_WIFI_DRIVER_H_ */