[PATCH 37/92] NAN: Handle followup frame after pairing is completed
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Wed Apr 22 05:23:28 PDT 2026
From: Avraham Stern <avraham.stern at intel.com>
After NAN pairing is completed, both sides send a followup frame with
their NIK. Parse the received NIK and add it to the cache together
with the NPK derived during pairing.
Signed-off-by: Avraham Stern <avraham.stern at intel.com>
---
src/nan/nan.c | 17 ++---
src/nan/nan.h | 18 ++++++
src/nan/nan_i.h | 3 +
src/nan/nan_pairing.c | 146 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 176 insertions(+), 8 deletions(-)
diff --git a/src/nan/nan.c b/src/nan/nan.c
index 30dcf6a614..9602f0553d 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -1303,7 +1303,7 @@ bool nan_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf,
size_t len, u8 req_instance_id, int handle)
{
struct nan_attrs attrs;
- bool ret;
+ bool ret = false;
if (nan_parse_attrs(nan, buf, len, &attrs)) {
wpa_printf(MSG_DEBUG,
@@ -1311,13 +1311,14 @@ bool nan_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf,
return false;
}
- if (!attrs.npba || !attrs.npba_len) {
- nan_attrs_clear(nan, &attrs);
- return false;
- }
-
- ret = nan_bootstrap_handle_rx(nan, addr, attrs.npba, attrs.npba_len,
- buf, len, handle, req_instance_id);
+ if (attrs.npba && attrs.npba_len)
+ ret = nan_bootstrap_handle_rx(nan, addr, attrs.npba,
+ attrs.npba_len, buf, len, handle,
+ req_instance_id);
+ else if (attrs.shared_key_desc)
+ ret = nan_pairing_followup_rx(nan, addr,
+ (void *)attrs.shared_key_desc,
+ attrs.shared_key_desc_len);
nan_attrs_clear(nan, &attrs);
return ret;
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 65075c1508..6e16e006c3 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -622,6 +622,24 @@ struct nan_config {
*/
int (*pairing_result_cb)(void *ctx, const u8 *peer_addr, int akmp,
int cipher, u8 status, struct wpa_ptk *ptk);
+
+ /**
+ * update_pairing_credentials - Report received NIK and NPK for a peer
+ *
+ * @ctx: Callback context from cb_ctx
+ * @nik: NAN Identity Key received from peer
+ * @nik_len: Length of the NIK
+ * @cipher_ver: Cipher version of the NIK
+ * @nik_lifetime: Lifetime of the NIK in seconds
+ * @akmp: AKMP suite used to establish the NPKSA
+ * @npk: the NPK associated with the received NIK
+ * @npk_len: Length of the NPK
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*update_pairing_credentials)(void *ctx, const u8 *nik,
+ size_t nik_len, int cipher_ver,
+ int nik_lifetime, int akmp, u8 *npk,
+ size_t npk_len);
};
struct nan_data * nan_init(const struct nan_config *cfg);
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 6762e3e83c..3e00aa7d9d 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -764,6 +764,9 @@ void nan_parse_peer_dev_capa_ext(struct nan_data *nan, struct nan_peer *peer,
int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nira_nonce,
u8 *nira_tag);
void nan_pairing_deinit_peer(struct nan_peer *peer);
+bool nan_pairing_followup_rx(struct nan_data *nan_data, const u8 *peer_addr,
+ struct nan_shared_key *shared_key_descr,
+ size_t attr_len);
#else
static inline
int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nira_nonce,
diff --git a/src/nan/nan_pairing.c b/src/nan/nan_pairing.c
index 01d0720f21..35c140ca9f 100644
--- a/src/nan/nan_pairing.c
+++ b/src/nan/nan_pairing.c
@@ -1042,3 +1042,149 @@ int nan_pairing_auth_rx(struct nan_data *nan_data,
return -1;
}
+
+
+/*
+ * nan_pairing_followup_rx - Process received NAN pairing follow-up frame
+ *
+ * @nan_data: NAN data context
+ * @peer_addr: MAC address of the peer device
+ * @shared_key_descr: Pointer to the shared key descriptor attribute
+ * @attr_len: Length of the shared key descriptor attribute
+ *
+ * Returns: true if the follow-up frame was processed, false otherwise.
+ *
+ * This function processes a received NAN pairing follow-up frame. It extracts
+ * the NIK (NAN Identity Key) from the frame and notifies about the received
+ * NIK.
+ * If the local device acted as the responder in the pairing process, it also
+ * sends the local NIK to the peer.
+ */
+bool nan_pairing_followup_rx(struct nan_data *nan_data, const u8 *peer_addr,
+ struct nan_shared_key *shared_key_descr,
+ size_t attr_len)
+{
+ struct nan_peer *peer;
+ struct pasn_data *pasn;
+ struct wpa_eapol_key *key_desc;
+ struct wpa_eapol_ie_parse ie;
+ struct nan_nik_kde *nik_kde;
+ struct nan_key_lifetime_kde *lifetime_kde;
+ u8 *pos;
+ struct wpabuf *key_data = NULL;
+ u16 key_data_len, key_info;
+ bool ret = false;
+
+ peer = nan_get_peer(nan_data, peer_addr);
+ if (!peer) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Follow-up frame from unknown peer");
+ return false;
+ }
+
+ pasn = peer->pairing.pasn;
+ if (!pasn || !pasn->ptk.kek_len) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: No PASN data for follow-up frame");
+ return false;
+ }
+
+ if (!nan_data->cfg->pairing_cfg.npk_caching) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: NPK caching not enabled, ignore follow-up frame");
+ return false;
+ }
+
+ key_desc = (void *)shared_key_descr->key;
+ key_info = WPA_GET_BE16(key_desc->key_info);
+
+ if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Follow-up frame does not contain pairwise key");
+ return false;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Follow-up frame does not contain encrypted key data");
+ return false;
+ }
+
+ if (attr_len < sizeof(*shared_key_descr) + sizeof(*key_desc) + 2) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Follow-up frame too short for key data length");
+ return false;
+ }
+
+ pos = shared_key_descr->key + sizeof(*key_desc);
+ key_data_len = WPA_GET_BE16(pos);
+
+ if (attr_len < sizeof(*shared_key_descr) + sizeof(*key_desc) + 2 + key_data_len) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Follow-up frame too short for key data");
+ return false;
+ }
+
+ pos += 2;
+
+ key_data = nan_crypto_decrypt_key_data(pasn->ptk.kek, pasn->ptk.kek_len,
+ pos, key_data_len);
+ if (!key_data) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Failed to decrypt key data in follow-up frame");
+ goto fail;
+ }
+
+ if (wpa_parse_kde_ies(wpabuf_head(key_data), wpabuf_len(key_data),
+ &ie) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Failed to parse decrypted key data in follow-up frame");
+ goto fail;
+ }
+
+ if (!ie.nan_nik) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: NIK KDE missing in decrypted key data");
+ goto fail;
+ }
+
+ nik_kde = (void *)ie.nan_nik;
+ if (nik_kde->cipher_ver != NAN_NIRA_CIPHER_VER_128) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Unsupported NIK cipher version: %u",
+ nik_kde->cipher_ver);
+ goto fail;
+ }
+
+ if (!ie.nan_key_lifetime) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Key Lifetime KDE missing in decrypted key data");
+ goto fail;
+ }
+
+ lifetime_kde = (void *)ie.nan_key_lifetime;
+ if (!(lifetime_kde->key_bitmap & NAN_KEY_LIFETIME_NIK)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Pairing: Unexpected key bitmap in Key "
+ "Lifetime KDE: 0x%02x",
+ lifetime_kde->key_bitmap);
+ goto fail;
+ }
+
+ nan_data->cfg->update_pairing_credentials(nan_data->cfg->cb_ctx,
+ nik_kde->nik, NAN_NIK_LEN,
+ nik_kde->cipher_ver,
+ be_to_host32(lifetime_kde->lifetime_sec),
+ pasn_get_akmp(pasn),
+ pasn_get_pmk(pasn),
+ pasn_get_pmk_len(pasn));
+
+ if (peer->pairing.self_pairing_role == NAN_PAIRING_ROLE_RESPONDER)
+ nan_send_nik(nan_data, peer);
+
+ ret = true;
+fail:
+ nan_pairing_deinit_peer(peer);
+ wpabuf_free(key_data);
+ return ret;
+}
--
2.53.0
More information about the Hostap
mailing list