[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