[PATCH 18/92] NAN: Add a function for triggering NAN pairing

Andrei Otcheretianski andrei.otcheretianski at intel.com
Wed Apr 22 05:23:09 PDT 2026


From: Avraham Stern <avraham.stern at intel.com>

Add a function for triggering NAN pairing. This function will setup
the PASN data and trigger sending PASN auth frame no. 1.

Signed-off-by: Avraham Stern <avraham.stern at intel.com>
---
 src/common/nan_defs.h |   2 +
 src/nan/nan.c         |   1 +
 src/nan/nan.h         |  23 +++
 src/nan/nan_i.h       |  52 ++++++
 src/nan/nan_pairing.c | 357 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 435 insertions(+)

diff --git a/src/common/nan_defs.h b/src/common/nan_defs.h
index 48a3bcf493..58bcb689df 100644
--- a/src/common/nan_defs.h
+++ b/src/common/nan_defs.h
@@ -564,4 +564,6 @@ struct nan_shared_key {
 #define NAN_NIRA_CIPHER_VER_128	0
 #define NAN_NIK_LEN		16
 
+#define NAN_PASN_SSID "516F9A010000"
+
 #endif /* NAN_DEFS_H */
diff --git a/src/nan/nan.c b/src/nan/nan.c
index ab8b997a3c..b49c4a4ad3 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -160,6 +160,7 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 	nan_ndl_reset(nan, peer);
 	nan_peer_flush_sec(&peer->info);
 	eloop_cancel_timeout(nan_peer_state_timeout, nan, peer);
+	nan_pairing_deinit_peer(peer);
 	os_free(peer);
 }
 
diff --git a/src/nan/nan.h b/src/nan/nan.h
index c5300aff41..41a24ab8c9 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -596,6 +596,16 @@ struct nan_config {
 	 * configured for the service or 0 if service is not found.
 	 */
 	u16 (*get_supported_bootstrap_methods)(void *ctx, int handle);
+
+	/**
+	 * send_pasn - Function handler to transmit a PASN auth frame
+	 *
+	 * @ctx: Callback context from cb_ctx
+	 * @data: Frame to transmit
+	 * @data_len: Length of frame to transmit
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_pasn)(void *ctx, const u8 *data, size_t data_len);
 };
 
 struct nan_data * nan_init(const struct nan_config *cfg);
@@ -668,12 +678,25 @@ struct wpabuf *nan_crypto_derive_nira_tag(const u8 *nik, size_t nik_len,
 					  const u8 *nira_nonce);
 #ifdef CONFIG_PASN
 int nan_pairing_add_attrs(struct nan_data *nan_data, struct wpabuf *buf);
+int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr,
+				   u8 auth_mode, int cipher, int handle,
+				   u8 peer_instance_id, bool responder,
+				   const char *password);
 #else
 static inline int nan_pairing_add_attrs(struct nan_data *nan_data,
 					struct wpabuf *buf)
 {
 	return 0;
 }
+
+static inline
+int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr,
+				   u8 auth_mode, int cipher, int handle,
+				   u8 peer_instance_id, bool responder,
+				   const char *password)
+{
+	return -1;
+}
 #endif /* CONFIG_PASN */
 
 #endif /* NAN_H */
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 4983184690..25ca5b011b 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -24,6 +24,8 @@ struct nan_config;
 #define NAN_KEK_MAX_LEN 32
 #define NAN_TK_MAX_LEN  32
 
+#define NAN_IE_MAX_SIZE 1024
+
 /**
  * struct nan_ptk - NAN Pairwise Transient Key
  * @kck: Key Confirmation Key
@@ -446,6 +448,49 @@ struct nan_bootstrap {
 	struct wpabuf *npba;
 };
 
+/**
+ * enum nan_pasn_auth_mode - NAN pairing authentication modes
+ *
+ * @NAN_PASN_AUTH_MODE_PASN: Unauthenticated PASN
+ * @NAN_PASN_AUTH_MODE_SAE: PASN authentication with SAE tunneling
+ * @NAN_PASN_AUTH_MODE_PMK: PASN authentication with PMK caching
+ */
+enum nan_pasn_auth_mode {
+	NAN_PASN_AUTH_MODE_PASN = 0,
+	NAN_PASN_AUTH_MODE_SAE = 1,
+	NAN_PASN_AUTH_MODE_PMK = 2,
+};
+
+/**
+ * enum nan_pairing_role - NAN pairing role types
+ *
+ * @NAN_PAIRING_ROLE_IDLE: No active pairing role
+ * @NAN_PAIRING_ROLE_INITIATOR: Device acting as pairing initiator
+ * @NAN_PAIRING_ROLE_RESPONDER: Device acting as pairing responder
+ */
+enum nan_pairing_role {
+	NAN_PAIRING_ROLE_IDLE,
+	NAN_PAIRING_ROLE_INITIATOR,
+	NAN_PAIRING_ROLE_RESPONDER,
+};
+
+/**
+ * struct nan_pairing_peer_data - NAN pairing peer information
+ *
+ * @pairing_cfg: NAN pairing configuration parameters
+ * @self_pairing_role: Role of this device in the pairing process
+ * @pasn: Pointer to PASN data
+ * @handle: Handle of the local service instance
+ * @peer_instance_id: Instance ID of the peer service
+ */
+struct nan_pairing_peer_data {
+	struct nan_pairing_cfg pairing_cfg;
+	enum nan_pairing_role self_pairing_role;
+	struct pasn_data *pasn;
+	int handle;
+	int peer_instance_id;
+};
+
 /**
  * struct nan_peer - Represents a known NAN peer
  * @list: List node for linking peers
@@ -458,6 +503,7 @@ struct nan_bootstrap {
  *     progress
  * @ndl: NDL data associated with this peer
  * @bootstrap: Bootstrap information of the peer
+ * @pairing: Pairing data associated with this peer
  */
 struct nan_peer {
 	struct dl_list list;
@@ -473,6 +519,8 @@ struct nan_peer {
 	struct nan_ndl *ndl;
 
 	struct nan_bootstrap bootstrap;
+
+	struct nan_pairing_peer_data pairing;
 };
 
 /**
@@ -702,6 +750,7 @@ int nan_add_nira_attr(struct wpabuf *buf, const u8 *nira_tag,
 #ifdef CONFIG_PASN
 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);
 #else
 static inline
 int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nira_nonce,
@@ -709,6 +758,9 @@ int nan_nira_get_tag_nonce(const struct nan_config *nan, u8 *nira_nonce,
 {
 	return -1;
 }
+
+static inline void nan_pairing_deinit_peer(struct nan_peer *peer)
+{}
 #endif /* CONFIG_PASN */
 
 #endif /* NAN_I_H */
diff --git a/src/nan/nan_pairing.c b/src/nan/nan_pairing.c
index 60c523c64e..3fb32631b0 100644
--- a/src/nan/nan_pairing.c
+++ b/src/nan/nan_pairing.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 #include "common.h"
 #include "nan/nan_i.h"
+#include "pasn/pasn_common.h"
 
 /*
  * nan_nira_get_tag_nonce - Generate NIRA nonce and compute NIRA tag
@@ -79,3 +80,359 @@ int nan_pairing_add_attrs(struct nan_data *nan, struct wpabuf *buf)
 
 	return 0;
 }
+
+
+void nan_pairing_deinit_peer(struct nan_peer *peer)
+{
+	if (!peer->pairing.pasn)
+		return;
+
+	wpa_pasn_reset(peer->pairing.pasn);
+	pasn_data_deinit(peer->pairing.pasn);
+	peer->pairing.pasn = NULL;
+	peer->pairing.self_pairing_role = NAN_PAIRING_ROLE_IDLE;
+}
+
+
+static bool nan_pairing_is_supported(struct nan_data *nan_data,
+				     struct nan_peer *peer, u8 auth_mode)
+{
+	if (auth_mode == NAN_PASN_AUTH_MODE_PASN ||
+	    auth_mode == NAN_PASN_AUTH_MODE_SAE) {
+		if (!nan_data->cfg->pairing_cfg.pairing_setup) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Device doesn't support pairing setup");
+			return false;
+		}
+
+		if (!peer->pairing.pairing_cfg.pairing_setup) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Peer doesn't support pairing setup");
+			return false;
+		}
+	} else if (auth_mode == NAN_PASN_AUTH_MODE_PMK) {
+		if (!nan_data->cfg->pairing_cfg.pairing_verification) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Device doesn't support pairing verification");
+			return false;
+		}
+
+		if (!peer->pairing.pairing_cfg.pairing_verification) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Peer doesn't support pairing verification");
+			return false;
+		}
+	}
+
+	return true;
+}
+
+
+static int nan_pairing_set_password(struct pasn_data *pasn,
+				    const char *passphrase)
+{
+#ifdef CONFIG_SAE
+	struct sae_pt *pt;
+
+	pasn->pasn_groups = os_calloc(2, sizeof(*pasn->pasn_groups));
+	if (!pasn->pasn_groups) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Pairing: Failed to allocate PASN groups");
+		return -1;
+	}
+
+	pasn->pasn_groups[0] = pasn->group;
+
+	pt = sae_derive_pt(pasn->pasn_groups, (const u8 *)NAN_PASN_SSID,
+			   os_strlen(NAN_PASN_SSID), (const u8 *)passphrase,
+			   os_strlen(passphrase), NULL, 0);
+	if (pasn_set_pt(pasn, pt) < 0) {
+		wpa_printf(MSG_DEBUG, "NAN: Pairing: Failed to set SAE pt");
+		sae_deinit_pt(pt);
+		return -1;
+	}
+
+	return 0;
+#else  /* CONFIG_SAE */
+	return -1;
+#endif /* CONFIG_SAE */
+}
+
+
+static struct wpabuf *nan_pairing_generate_rsnxe(int akmp)
+{
+	/* According to Wi-Fi Aware Specification version 4.0, Table 26,
+	 * the RSNXE's capabilities field in NAN PASN frames is 16 bits long
+	 */
+	u16 capab = 1; /* bit 0-3 = Field length (n - 1) */
+
+	struct wpabuf *buf;
+
+	if (wpa_key_mgmt_sae(akmp))
+		capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+
+	/* IE header (2 octets) + capabilities field (2 octets) */
+	buf = wpabuf_alloc(4);
+	if (!buf)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "RSNXE capabilities: %04x", capab);
+	wpabuf_put_u8(buf, WLAN_EID_RSNX);
+	wpabuf_put_u8(buf, 2);
+	wpabuf_put_le16(buf, capab);
+	return buf;
+}
+
+
+static int nan_pairing_send_cb(void *ctx, const u8 *data, size_t data_len,
+			       int noack, unsigned int freq, unsigned int wait)
+{
+	struct nan_data *nan_data = (struct nan_data *)ctx;
+
+	return nan_data->cfg->send_pasn(nan_data->cfg->cb_ctx, data, data_len);
+}
+
+
+static int nan_pairing_pasn_initialize(struct nan_data *nan_data,
+				       struct nan_peer *peer, u8 auth_mode,
+				       int cipher, const char *password,
+				       enum nan_pairing_role self_role)
+{
+	struct wpabuf *rsnxe = NULL;
+	struct pasn_data *pasn;
+	struct nan_pairing_peer_data *pairing;
+
+	pairing = &peer->pairing;
+	if (pairing->pasn) {
+		wpa_pasn_reset(pairing->pasn);
+	} else {
+		pairing->pasn = pasn_data_init();
+		if (!pairing->pasn) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Failed to initialize PASN data");
+			return -1;
+		}
+	}
+
+	pasn = pairing->pasn;
+	pasn_set_own_addr(pasn, nan_data->cfg->nmi_addr);
+	pasn_set_peer_addr(pasn, peer->nmi_addr);
+	pasn_set_bssid(pasn, nan_data->cluster_id);
+
+	if (self_role == NAN_PAIRING_ROLE_INITIATOR)
+		pasn->pmksa = nan_data->initiator_pmksa;
+	else
+		pasn->pmksa = nan_data->responder_pmksa;
+
+	if (cipher == WPA_CIPHER_GCMP_256 &&
+	    (nan_data->cfg->pairing_cfg.cipher_suites & NAN_PAIRING_PASN_256)) {
+		pasn->group = 20;
+		pasn->cipher = WPA_CIPHER_GCMP_256;
+	} else if (cipher == WPA_CIPHER_CCMP &&
+		   (nan_data->cfg->pairing_cfg.cipher_suites &
+		    NAN_PAIRING_PASN_128)) {
+		pasn->group = 19;
+		pasn->cipher = WPA_CIPHER_CCMP;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Pairing: Unsupported cipher suite %s",
+			   wpa_cipher_txt(cipher));
+		goto fail;
+	}
+
+	pasn_enable_kdk_derivation(pasn);
+
+	if (auth_mode == NAN_PASN_AUTH_MODE_SAE) {
+		pasn_set_akmp(pasn, WPA_KEY_MGMT_SAE);
+		if (!password) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Password not available");
+			goto fail;
+		}
+
+		if (nan_pairing_set_password(pasn, password) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Failed to set password");
+			goto fail;
+		}
+	} else if (auth_mode == NAN_PASN_AUTH_MODE_PASN) {
+		pasn_set_akmp(pasn, WPA_KEY_MGMT_PASN);
+		pasn_set_noauth(pasn, true);
+
+		/* Set allowed PASN groups for the responder to validate */
+		pasn->pasn_groups = os_calloc(2, sizeof(*pasn->pasn_groups));
+		if (!pasn->pasn_groups) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Failed to allocate PASN groups");
+			goto fail;
+		}
+		pasn->pasn_groups[0] = pasn->group;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Pairing: Unsupported authentication mode %u",
+			   auth_mode);
+		goto fail;
+	}
+
+	pasn_set_rsn_pairwise(pasn, pasn->cipher);
+	pasn_set_wpa_key_mgmt(pasn, pasn->akmp);
+
+	if (auth_mode != NAN_PASN_AUTH_MODE_PASN) {
+		rsnxe = nan_pairing_generate_rsnxe(pasn->akmp);
+		if (!rsnxe) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Pairing: Failed to generate RSNXE");
+			goto fail;
+		}
+
+		pasn_set_rsnxe_ie(pairing->pasn, wpabuf_head_u8(rsnxe));
+		wpabuf_free(rsnxe);
+	}
+
+	pasn_register_callbacks(pasn, nan_data, nan_pairing_send_cb, NULL,
+				NULL, NULL);
+	return 0;
+
+fail:
+	pasn_data_deinit(pasn);
+	pairing->pasn = NULL;
+	return -1;
+}
+
+
+/*
+ * nan_pairing_prepare_pasn_elems - Prepare NAN IE for pairing PASN frames
+ *
+ * @nan_data: Pointer to NAN data structure
+ * @peer: Pointer to NAN peer structure
+ * @extra_ies: Buffer to append the NAN IE to
+ * @publish_id: Publish ID to use in the CSIA
+ *
+ * This function adds a NAN IE containing the NAN attributes that shall be
+ * included in the first and second PASN frames for NAN pairing.
+ * The attributes added are:
+ * - Device Capability Extension attribute (DCEA)
+ * - Cipher suite information attribute (CSIA) with appropriate PASN cipher
+ *   (either GCMP-256 or GCMP-128)
+ * - NAN Pairing Bootstrapping Attribute (NPBA) if available
+ */
+static void nan_pairing_prepare_pasn_elems(struct nan_data *nan_data,
+					   struct nan_peer *peer,
+					   struct wpabuf *extra_ies,
+					   int publish_id)
+{
+	u8 *len_ptr;
+	struct nan_cipher_suite cs;
+	size_t initial_len = wpabuf_len(extra_ies);
+
+	wpabuf_put_u8(extra_ies, WLAN_EID_VENDOR_SPECIFIC);
+
+	/* placeholder for length - to be filled later */
+	len_ptr = wpabuf_put(extra_ies, 1);
+
+	/* OUI + OUI Type */
+	wpabuf_put_be32(extra_ies, NAN_IE_VENDOR_TYPE);
+
+	nan_add_dev_capa_ext_attr(nan_data, extra_ies);
+	if (peer->pairing.pasn->cipher == WPA_CIPHER_GCMP_256)
+		cs.csid = NAN_CS_PK_PASN_256;
+	else
+		cs.csid = NAN_CS_PK_PASN_128;
+
+	cs.instance_id = publish_id;
+
+	/*
+	 * TODO: get security capabilities from somewhere. For now, it doesn't
+	 * matter as the capability field is not used in pairing anyway
+	 */
+	nan_add_csia(extra_ies, 0, 1, &cs);
+
+	if (peer->bootstrap.npba)
+		wpabuf_put_buf(extra_ies, peer->bootstrap.npba);
+
+	*len_ptr = wpabuf_len(extra_ies) - initial_len - 2;
+}
+
+
+/*
+ * nan_pairing_initiate_pasn_auth - Initiate PASN authentication for NAN pairing
+ *
+ * @nan_data: NAN data context
+ * @addr: MAC address of the peer device
+ * @auth_mode: Authentication mode to be used (PASN, SAE or PMK)
+ * @cipher: Cipher suite to be used for the pairing
+ * @handle: Handle of the service instance for which pairing is requested
+ * @peer_instance_id: Instance ID of the peer service for which pairing is
+ *	requested
+ * @responder: Whether this device is acting as PASN responder
+ * @password: Password to be used for authentication (if applicable)
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr,
+				   u8 auth_mode, int cipher, int handle,
+				   u8 peer_instance_id, bool responder,
+				   const char *password)
+{
+	int ret = 0;
+	struct pasn_data *pasn;
+	struct nan_peer *peer;
+	struct wpabuf *extra_ies;
+
+	if (!addr) {
+		wpa_printf(MSG_DEBUG, "NAN: Pairing: Peer address missing");
+		return -1;
+	}
+
+	peer = nan_get_peer(nan_data, addr);
+	if (!peer) {
+		wpa_printf(MSG_DEBUG, "NAN: Pairing: Peer not known");
+		return -1;
+	}
+
+	if (!nan_pairing_is_supported(nan_data, peer, auth_mode)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Pairing: Invalid params to initiate Authentication");
+		return -1;
+	}
+
+	peer->pairing.self_pairing_role =
+	    responder ? NAN_PAIRING_ROLE_RESPONDER : NAN_PAIRING_ROLE_INITIATOR;
+
+	if (nan_pairing_pasn_initialize(nan_data, peer, auth_mode, cipher,
+					password,
+					peer->pairing.self_pairing_role)) {
+		wpa_printf(MSG_DEBUG, "NAN: Pairing: Initialize failed");
+		return -1;
+	}
+
+	pasn = peer->pairing.pasn;
+
+	extra_ies = wpabuf_alloc(NAN_IE_MAX_SIZE);
+	if (!extra_ies)
+		return -1;
+
+	/* TODO: Add support for NAN IE fragmentation if it's larger than 255
+	 * octets, as defined in Wi-Fi Aware Specification v4.0 Section 9.1
+	 */
+	nan_pairing_prepare_pasn_elems(nan_data, peer, extra_ies, handle);
+	pasn_set_extra_ies(pasn, wpabuf_head_u8(extra_ies),
+			   wpabuf_len(extra_ies));
+	wpabuf_free(extra_ies);
+
+	peer->pairing.handle = handle;
+	peer->pairing.peer_instance_id = peer_instance_id;
+
+	if (responder)
+		return 0;
+
+	ret = wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr,
+			      pasn->bssid, pasn->akmp, pasn->cipher,
+			      pasn->group, 0, NULL, 0, NULL, 0, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "NAN: Pairing: Failed to start PASN");
+		nan_pairing_deinit_peer(peer);
+	}
+
+	return ret;
+}
-- 
2.53.0




More information about the Hostap mailing list