[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