fix(nan): clear group keys on NAN stop and peer NDP teardown

Fix group-key lifecycle gaps on the secured NDP path:

- On NAN stop, reset the device-global IGTK/BIGTK state via new
  nan_security_reset_own_group_keys() so the next start regenerates fresh
  keys. Previously the one-shot nan_ensure_own_igtk/bigtk kept own_*_set,
  so restart re-installed the stale key with IPN/BIPN=0, resetting the
  blob's monotonic replay counter (and reusing keys if the NMI changed).

- On peer teardown (nan_app_clear_one_peer_tks), remove the peer RX
  IGTK/BIGTK from the blob (they were installed against the peer NMI at
  NDP confirm) and scrub ndl->igtk/bigtk + flags. Previously only the GTK
  was removed, leaving stale BIP keys installed and key bytes in memory.

- Copy the GTK Key RSC into the descriptor only when a GTK KDE is present,
  matching the comment and avoiding stale RSC on an IGTK/BIGTK-only path.
This commit is contained in:
Sarvesh Bodakhe
2026-07-01 14:26:28 +05:30
committed by BOT
parent c64ec928f4
commit 96a421e851
3 changed files with 51 additions and 2 deletions

View File

@@ -272,11 +272,28 @@ static void nan_app_clear_one_peer_tks(const uint8_t *peer_nmi)
key_rsc, sizeof(key_rsc),
NULL, 0, NAN_KEY_ND_GTK);
/* Drop the peer's RX IGTK/BIGTK (BIP-CMAC-128, installed against the
* peer NMI at NDP confirm) so no stale BIP keys linger in the blob. */
if (ndl->igtk_set) {
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
(uint8_t *)peer_nmi, ndl->igtk_keyid, 0,
key_rsc, 6,
NULL, 0, NAN_KEY_ND_IGTK);
}
if (ndl->bigtk_set) {
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
(uint8_t *)peer_nmi, ndl->bigtk_keyid, 0,
key_rsc, 6,
NULL, 0, NAN_KEY_ND_BIGTK);
}
forced_memzero(ndl->nd_tk, sizeof(ndl->nd_tk));
forced_memzero(ndl->nd_kck, sizeof(ndl->nd_kck));
forced_memzero(ndl->nd_kek, sizeof(ndl->nd_kek));
forced_memzero(ndl->gtk, sizeof(ndl->gtk));
forced_memzero(ndl->own_gtk, sizeof(ndl->own_gtk));
forced_memzero(ndl->igtk, sizeof(ndl->igtk));
forced_memzero(ndl->bigtk, sizeof(ndl->bigtk));
ndl->ptk_set = 0;
ndl->tk_len = 0;
ndl->kck_len = 0;
@@ -285,6 +302,10 @@ static void nan_app_clear_one_peer_tks(const uint8_t *peer_nmi)
ndl->own_gtk_set = 0;
ndl->gtk_len = 0;
ndl->own_gtk_len = 0;
ndl->igtk_set = 0;
ndl->bigtk_set = 0;
ndl->igtk_len = 0;
ndl->bigtk_len = 0;
}
/* Fallback when no NDL slot tracks peer_ndi yet. */
@@ -1868,6 +1889,13 @@ void esp_nan_action_stop(void)
nan_app_clear_paired_peers();
#endif
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/* Drop the device-global IGTK/BIGTK so the next start regenerates fresh
* keys instead of re-installing a stale key with IPN/BIPN=0 (which would
* reset the blob's replay counter) — see nan_security_reset_own_group_keys. */
nan_security_reset_own_group_keys();
#endif
esp_nan_internal_register_callbacks(NULL);
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);

View File

@@ -529,6 +529,12 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
* Call once at NAN start; never per-NDP (would reset the blob's BIPN counter). */
void nan_security_install_own_group_integrity_keys(void);
/* Clear the device-global IGTK/BIGTK state on NAN stop so the next NAN start
* regenerates fresh keys. Prevents re-installing a stale key with IPN/BIPN=0
* (which resets the blob's monotonic replay counter) and key reuse if the NMI
* is re-randomized on restart. */
void nan_security_reset_own_group_keys(void);
/*
* Match subscriber discovery security to a publisher's params.
* NCS-SK: Compare locally derived ND-PMKID (subscriber passphrase, publisher NMI) to

View File

@@ -765,6 +765,19 @@ void nan_security_install_own_group_integrity_keys(void)
}
}
void nan_security_reset_own_group_keys(void)
{
forced_memzero(s_nan_ctx.own_igtk, sizeof(s_nan_ctx.own_igtk));
memset(s_nan_ctx.own_igtk_ipn, 0, sizeof(s_nan_ctx.own_igtk_ipn));
s_nan_ctx.own_igtk_keyid = 0;
s_nan_ctx.own_igtk_set = false;
forced_memzero(s_nan_ctx.own_bigtk, sizeof(s_nan_ctx.own_bigtk));
memset(s_nan_ctx.own_bigtk_bipn, 0, sizeof(s_nan_ctx.own_bigtk_bipn));
s_nan_ctx.own_bigtk_keyid = 0;
s_nan_ctx.own_bigtk_set = false;
}
/* NAN KDE header (802.11 Fig 12-34: DD len OUI(3) DataType(1)); mirrors hostap
* nan_add_kde_hdr(). data_len excludes the DD/len/OUI/type bytes. */
static uint8_t *nan_kde_put_hdr(uint8_t *p, uint8_t data_type, uint8_t data_len)
@@ -962,8 +975,10 @@ static int nan_append_own_group_kdes(struct ndl_info *ndl, uint8_t *key_desc, si
key_desc[NAN_KEY_DESC_DATA_LEN_OFF] = (wrapped_len >> 8) & 0xFF;
key_desc[NAN_KEY_DESC_DATA_LEN_OFF + 1] = wrapped_len & 0xFF;
/* Key RSC carries the GTK's RSC when a GTK KDE is present (§7.1.3.5). */
memcpy(&key_desc[NAN_KEY_DESC_RSC_OFF], ndl->own_gtk_rsc, NAN_KEY_RSC_LEN);
/* Key RSC carries the GTK's RSC only when a GTK KDE is present (§7.1.3.5). */
if (want_gtk) {
memcpy(&key_desc[NAN_KEY_DESC_RSC_OFF], ndl->own_gtk_rsc, NAN_KEY_RSC_LEN);
}
ESP_LOGI(TAG, "Group Key Data wrapped: %u plain -> %u wrapped bytes, KDEs=[%s%s%s]",
(unsigned)plain_len, (unsigned)wrapped_len,