mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(nan): Add support for NAN Pairing Verification
- Add nira attr and verification for pasn auth frames - Refine key clearing and pairing complete logic for pasn verify - Add NIRA own-service resolution, cached NIK checks, and dynamic pairing IE construction for bootstrap vs verify paths. - Replace NAN bootstrap events by private callbacks
This commit is contained in:
@@ -106,6 +106,7 @@ struct nan_cb_peer_info {
|
||||
uint16_t ssi_len; /**< SSI length in bytes */
|
||||
wifi_nan_peer_sdf_security_t *peer_security_params; /**< Peer's discovery security params parsed from SDF */
|
||||
nan_vendor_ie_t *vendor_ie; /**< Vendor-specific IE, if any */
|
||||
bool nira_verified; /**< true when received NIRA tag verified against cached NIK */
|
||||
};
|
||||
|
||||
/* NDP Peer info parsed from NAF. */
|
||||
@@ -196,6 +197,7 @@ struct nan_sync_callbacks {
|
||||
uint32_t (* get_nira_len)(void);
|
||||
int (* construct_nira)(uint8_t *frm);
|
||||
bool (*verify_nira)(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
|
||||
bool (*peer_nik_cached)(uint8_t *peer_mac);
|
||||
};
|
||||
|
||||
/* Host helpers for NAN encrypted-datapath, registered via
|
||||
@@ -1201,6 +1203,19 @@ uint32_t esp_nan_get_nira_len(void);
|
||||
*/
|
||||
int esp_nan_construct_nira(uint8_t *frm);
|
||||
|
||||
/**
|
||||
* @brief Construct a NAN Cipher Suite Info Attribute (CSIA)
|
||||
*
|
||||
* @param[out] frm Buffer to write the attribute to
|
||||
* @param[in] pub_id Publish service instance id
|
||||
* @param[in] own_csid_bitmap Locally supported cipher suite bitmap
|
||||
* @param[in] peer_csid_bitmap Peer cipher suite bitmap, or 0 to use own bitmap
|
||||
*
|
||||
* @return Number of bytes written, or 0 on failure/no cipher suite
|
||||
*/
|
||||
int esp_nan_construct_csia(uint8_t *frm, uint8_t pub_id,
|
||||
uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
|
||||
|
||||
/**
|
||||
* @brief Verify a received NAN Identity Resolution Attribute (NIRA)
|
||||
*
|
||||
@@ -1212,6 +1227,24 @@ int esp_nan_construct_nira(uint8_t *frm);
|
||||
*/
|
||||
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
|
||||
|
||||
/**
|
||||
* @brief Verify a received NIRA and resolve the matched own service id
|
||||
*
|
||||
* Behaves like @ref esp_nan_verify_nira but additionally outputs the local
|
||||
* service instance id the verifying NIK maps to, used to anchor a pairing
|
||||
* verify-session flag. @p own_inst_id is set to 0 when the identity does not
|
||||
* resolve to an active local service.
|
||||
*
|
||||
* @param[in] peer_mac NMI of the sender
|
||||
* @param[in] nira_attr NIRA attribute buffer
|
||||
* @param[in] nira_attr_len Attribute length in bytes
|
||||
* @param[out] own_inst_id Resolved own service instance id (0 if none)
|
||||
*
|
||||
* @return true if the tag matches, false otherwise
|
||||
*/
|
||||
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
|
||||
uint16_t nira_attr_len, uint8_t *own_inst_id);
|
||||
|
||||
/**
|
||||
* @brief Get the time information from the MAC clock. The time is precise only if modem sleep or light sleep is not enabled.
|
||||
*
|
||||
|
||||
@@ -1305,8 +1305,6 @@ typedef enum {
|
||||
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
|
||||
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
|
||||
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
|
||||
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
|
||||
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN pairing completed after NIK follow-up exchange */
|
||||
WIFI_EVENT_NAN_CLUSTER_JOIN, /**< Posted when the device joins, starts, or merges into a NAN cluster */
|
||||
@@ -1637,38 +1635,6 @@ typedef struct {
|
||||
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
|
||||
} wifi_event_ndp_terminated_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Request is received from a peer.
|
||||
* The application should respond using esp_wifi_nan_bootstrap_response().
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t selected_method; /**< Bootstrapping method selected by initiator (one WIFI_NAN_BOOTSTRAP_* bit) */
|
||||
uint8_t is_comeback; /**< 1 if this is a comeback retry with cookie */
|
||||
uint32_t cookie; /**< Comeback cookie from initiator (0 if none) */
|
||||
} wifi_event_nan_bootstrap_indication_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Response is received,
|
||||
* or when the bootstrapping handshake completes/fails.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t status; /**< 0=Accepted, 1=Rejected, 2=Comeback (wifi_nan_pairing_status_t) */
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
|
||||
uint8_t reason_code; /**< Rejection reason (valid if rejected) */
|
||||
uint16_t comeback_after; /**< Comeback deferral time in TUs (valid if comeback) */
|
||||
uint32_t cookie; /**< Comeback cookie from responder (0 if none) */
|
||||
} wifi_event_nan_bootstrap_complete_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
|
||||
*/
|
||||
|
||||
+1
-1
Submodule components/esp_wifi/lib updated: 9ff50d7c69...36bb033d2f
@@ -1305,8 +1305,6 @@ typedef enum {
|
||||
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
|
||||
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
|
||||
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
|
||||
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
|
||||
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN pairing completed after NIK follow-up exchange */
|
||||
WIFI_EVENT_NAN_CLUSTER_JOIN, /**< Posted when the device joins, starts, or merges into a NAN cluster */
|
||||
@@ -1637,38 +1635,6 @@ typedef struct {
|
||||
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
|
||||
} wifi_event_ndp_terminated_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Request is received from a peer.
|
||||
* The application should respond using esp_wifi_nan_bootstrap_response().
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t selected_method; /**< Bootstrapping method selected by initiator (one WIFI_NAN_BOOTSTRAP_* bit) */
|
||||
uint8_t is_comeback; /**< 1 if this is a comeback retry with cookie */
|
||||
uint32_t cookie; /**< Comeback cookie from initiator (0 if none) */
|
||||
} wifi_event_nan_bootstrap_indication_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Response is received,
|
||||
* or when the bootstrapping handshake completes/fails.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t status; /**< 0=Accepted, 1=Rejected, 2=Comeback (wifi_nan_pairing_status_t) */
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
|
||||
uint8_t reason_code; /**< Rejection reason (valid if rejected) */
|
||||
uint16_t comeback_after; /**< Comeback deferral time in TUs (valid if comeback) */
|
||||
uint32_t cookie; /**< Comeback cookie from responder (0 if none) */
|
||||
} wifi_event_nan_bootstrap_complete_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
|
||||
*/
|
||||
|
||||
@@ -53,6 +53,8 @@ void esp_nan_action_stop(void);
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
|
||||
#include "esp_private/wifi_types.h"
|
||||
|
||||
#define WIFI_NAN_PAIRING_REASON_NIK_FUP_TIMEOUT 1 /**< Local reason: peer NIK follow-up not received within timeout.
|
||||
See Wi-Fi Aware v4.0 §7.6.4.2 for the NIK-exchange procedure. */
|
||||
|
||||
@@ -76,9 +78,46 @@ typedef struct {
|
||||
uint8_t peer_svc_id;
|
||||
uint8_t peer_nmi[6];
|
||||
enum nan_pairing_role self_role;
|
||||
uint8_t pairing_verification: 1; /**< 1 - PASN verify (re-pair), 0 - PASN auth (bootstrap pairing) */
|
||||
uint8_t reserved: 7;
|
||||
union pairing_cred_t cred;
|
||||
} wifi_nan_pairing_config_t;
|
||||
|
||||
/**
|
||||
* @brief NAN Pairing Bootstrap frame event (request or response).
|
||||
*
|
||||
* @p type is WIFI_NAN_NPBA_TYPE_REQUEST (1) for a bootstrapping request from a peer,
|
||||
* or WIFI_NAN_NPBA_TYPE_RESPONSE (2) for a bootstrapping response.
|
||||
* For requests, @p methods is the selected bootstrapping method.
|
||||
* For responses, @p methods is the matched method, @p status and @p reason_code are valid.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t type; /**< WIFI_NAN_NPBA_TYPE_REQUEST or WIFI_NAN_NPBA_TYPE_RESPONSE */
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t methods; /**< selected_method (request) or matched_method (response) */
|
||||
uint8_t status; /**< wifi_nan_pairing_status_t; valid for response */
|
||||
uint8_t reason_code; /**< Rejection reason; valid for response when rejected */
|
||||
} wifi_nan_bootstrap_event_t;
|
||||
|
||||
/**
|
||||
* @brief Callback invoked when a NAN Pairing Bootstrap request or response is received.
|
||||
*
|
||||
* @param evt Bootstrap frame event data.
|
||||
*/
|
||||
typedef void (*esp_nan_app_bootstrap_cb_t)(const wifi_nan_bootstrap_event_t *evt);
|
||||
|
||||
/**
|
||||
* @brief Set the callback for NAN bootstrap request/response frames.
|
||||
*
|
||||
* @param cb Bootstrap callback, or NULL to clear.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: succeed
|
||||
*/
|
||||
esp_err_t esp_nan_app_set_bootstrap_cb(esp_nan_app_bootstrap_cb_t cb);
|
||||
|
||||
/**
|
||||
* @brief NAN Pairing Bootstrapping status values
|
||||
*/
|
||||
@@ -141,7 +180,7 @@ esp_err_t esp_wifi_nan_bootstrap_request(wifi_nan_pairing_bootstrap_req_t *req);
|
||||
* @brief Respond to a NAN Pairing Bootstrapping request from a peer
|
||||
*
|
||||
* @attention This API should be called by the Publisher after receiving a
|
||||
* WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event.
|
||||
* bootstrap indication via the registered NAN bootstrap callback.
|
||||
*
|
||||
* @param resp Pairing bootstrapping response parameters.
|
||||
*
|
||||
@@ -185,6 +224,24 @@ struct nan_pasn_data *esp_nan_app_get_pasn_data(void);
|
||||
*/
|
||||
void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd);
|
||||
|
||||
/**
|
||||
* @brief Clear NM-TK and ND-TK in firmware and host state.
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 octets), or NULL to clear all peers on
|
||||
* @p service_id.
|
||||
* @param service_id Own service id; used only when @p peer_nmi is NULL.
|
||||
*/
|
||||
void esp_nan_app_clear_peer_tks(const uint8_t *peer_nmi, uint8_t service_id);
|
||||
|
||||
/**
|
||||
* @brief Terminate all active NDPs with a peer (PASN verify prep).
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 octets).
|
||||
*
|
||||
* @return ESP_OK if all ends succeeded or none were active.
|
||||
*/
|
||||
esp_err_t esp_nan_app_end_peer_datapaths(const uint8_t *peer_nmi);
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_private/wifi.h"
|
||||
#include "esp_wifi_netif.h"
|
||||
@@ -43,6 +44,18 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
|
||||
(void)nira_attr_len;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
|
||||
uint16_t nira_attr_len, uint8_t *own_inst_id)
|
||||
{
|
||||
(void)peer_mac;
|
||||
(void)nira_attr;
|
||||
(void)nira_attr_len;
|
||||
if (own_inst_id) {
|
||||
*own_inst_id = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
|
||||
@@ -220,6 +233,103 @@ void nan_app_clear_paired_peers(void)
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
||||
|
||||
static void nan_app_clear_one_peer_tks(const uint8_t *peer_nmi)
|
||||
{
|
||||
uint8_t key_rsc[8] = {0};
|
||||
static const uint8_t zero_mac[6] = {0};
|
||||
|
||||
if (!peer_nmi || memcmp(peer_nmi, zero_mac, 6) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
|
||||
/* NM-TK is bound to peer NMI (see nan_pasn_install_nan_pairwise_tk). */
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
(uint8_t *)peer_nmi, 1, 1,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_NM_TK);
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
struct ndl_info *ndl = nan_find_ndl(0, (uint8_t *)peer_nmi);
|
||||
uint8_t *key_addr = (uint8_t *)peer_nmi;
|
||||
|
||||
if (ndl) {
|
||||
if (memcmp(ndl->peer_ndi, zero_mac, 6) != 0) {
|
||||
key_addr = ndl->peer_ndi;
|
||||
}
|
||||
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
key_addr, 0, 1,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_ND_TK);
|
||||
|
||||
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));
|
||||
ndl->ptk_set = 0;
|
||||
ndl->tk_len = 0;
|
||||
ndl->kck_len = 0;
|
||||
ndl->kek_len = 0;
|
||||
}
|
||||
|
||||
/* Fallback when no NDL slot tracks peer_ndi yet. */
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
(uint8_t *)peer_nmi, 0, 1,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_ND_TK);
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
||||
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
|
||||
void esp_nan_app_clear_peer_tks(const uint8_t *peer_nmi, uint8_t service_id)
|
||||
{
|
||||
if (peer_nmi) {
|
||||
nan_app_clear_one_peer_tks(peer_nmi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (service_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
uint8_t peer_nmis[NAN_MAX_PEERS_RECORD][MACADDR_LEN];
|
||||
int peer_count = 0;
|
||||
struct own_svc_info *p_own_svc;
|
||||
struct peer_svc_info *temp;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
p_own_svc = nan_find_own_svc(service_id);
|
||||
if (!p_own_svc) {
|
||||
NAN_DATA_UNLOCK();
|
||||
return;
|
||||
}
|
||||
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
|
||||
bool dup = false;
|
||||
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
if (MACADDR_EQUAL(peer_nmis[i], temp->peer_nmi)) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dup && peer_count < NAN_MAX_PEERS_RECORD) {
|
||||
MACADDR_COPY(peer_nmis[peer_count], temp->peer_nmi);
|
||||
peer_count++;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
nan_app_clear_one_peer_tks(peer_nmis[i]);
|
||||
}
|
||||
#else
|
||||
(void)service_id;
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr)
|
||||
@@ -496,9 +606,12 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_
|
||||
if (pairing) {
|
||||
memcpy(&p_svc->pairing, pairing, sizeof(*pairing));
|
||||
}
|
||||
#else
|
||||
(void)pairing;
|
||||
#endif
|
||||
#else
|
||||
(void)security_cfg;
|
||||
(void)pairing;
|
||||
#endif
|
||||
return p_svc;
|
||||
}
|
||||
@@ -843,6 +956,9 @@ static void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *pe
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
evt->bootstrapping_methods = nan_app_parse_npba_from_publish(npba);
|
||||
if (peer_info->nira_verified) {
|
||||
evt->already_paired = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
evt->ssi_version = ssi_ver;
|
||||
@@ -869,13 +985,31 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
|
||||
}
|
||||
uint8_t sub_id = peer_info->peer_svc_id;
|
||||
uint8_t *sub_nmi = peer_info->peer_mac;
|
||||
|
||||
uint8_t *ssi = peer_info->ssi;
|
||||
uint16_t ssi_len = peer_info->ssi_len;
|
||||
uint32_t device_caps = peer_info->device_caps;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
if (!nan_find_peer_svc(pub_id, sub_id, sub_nmi)) {
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(pub_id, sub_id, sub_nmi);
|
||||
if (!p_peer_svc) {
|
||||
p_peer_svc = nan_find_peer_svc(pub_id, 0, sub_nmi);
|
||||
}
|
||||
if (!p_peer_svc) {
|
||||
nan_record_peer_svc(pub_id, sub_id, sub_nmi, device_caps);
|
||||
} else {
|
||||
if (p_peer_svc->svc_id != sub_id) {
|
||||
p_peer_svc->svc_id = sub_id;
|
||||
}
|
||||
if (p_peer_svc->own_svc_id != pub_id) {
|
||||
p_peer_svc->own_svc_id = pub_id;
|
||||
}
|
||||
if (p_peer_svc->device_caps != device_caps) {
|
||||
p_peer_svc->device_caps = device_caps;
|
||||
}
|
||||
if (!MACADDR_EQUAL(p_peer_svc->peer_nmi, sub_nmi)) {
|
||||
MACADDR_COPY(p_peer_svc->peer_nmi, sub_nmi);
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
@@ -915,8 +1049,25 @@ static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_inf
|
||||
uint32_t device_caps = peer_info->device_caps;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
if (!nan_find_peer_svc(svc_id, peer_svc_id, peer_mac)) {
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(svc_id, peer_svc_id, peer_mac);
|
||||
if (!p_peer_svc) {
|
||||
p_peer_svc = nan_find_peer_svc(svc_id, 0, peer_mac);
|
||||
}
|
||||
if (!p_peer_svc) {
|
||||
nan_record_peer_svc(svc_id, peer_svc_id, peer_mac, device_caps);
|
||||
} else {
|
||||
if (p_peer_svc->svc_id != peer_svc_id) {
|
||||
p_peer_svc->svc_id = peer_svc_id;
|
||||
}
|
||||
if (p_peer_svc->own_svc_id != svc_id) {
|
||||
p_peer_svc->own_svc_id = svc_id;
|
||||
}
|
||||
if (p_peer_svc->device_caps != device_caps) {
|
||||
p_peer_svc->device_caps = device_caps;
|
||||
}
|
||||
if (!MACADDR_EQUAL(p_peer_svc->peer_nmi, peer_mac)) {
|
||||
MACADDR_COPY(p_peer_svc->peer_nmi, peer_mac);
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
@@ -931,6 +1082,7 @@ static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_inf
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
|
||||
if (npba) {
|
||||
nan_app_parse_npba_from_receive(svc_id, peer_svc_id, peer_mac, npba);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -999,8 +1151,8 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
|
||||
nan_record_peer_svc(pub_id, 0, peer_nmi, device_caps);
|
||||
}
|
||||
|
||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||
if (ndl && peer_ndi) {
|
||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, peer_nmi);
|
||||
if (ndl) {
|
||||
MACADDR_COPY(ndl->peer_ndi, peer_ndi);
|
||||
}
|
||||
|
||||
@@ -1311,6 +1463,7 @@ static void nan_action_txdone_cb(uint32_t context, bool tx_status)
|
||||
|
||||
static void esp_nan_ndp_tx_done_cb(uint8_t ndp_id, const uint8_t *peer_nmi, uint8_t msg_type, bool tx_status)
|
||||
{
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
|
||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||
@@ -1440,6 +1593,71 @@ void esp_nan_app_init(void)
|
||||
esp_nan_internal_register_secure_dp_funcs(&s_nan_secure_dp_funcs);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
|
||||
static bool nan_peer_cred_npk_present(const wifi_nan_peer_creds_t *c)
|
||||
{
|
||||
static const uint8_t zero_npk[ESP_WIFI_NAN_NPK_LEN] = {0};
|
||||
|
||||
if (!c || !c->is_valid) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(c->npk, zero_npk, ESP_WIFI_NAN_NPK_LEN) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool nan_peer_nik_cached_cb(uint8_t *peer_mac)
|
||||
{
|
||||
bool cached = false;
|
||||
struct peer_svc_info *peer;
|
||||
|
||||
if (!peer_mac) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
peer = nan_find_peer_svc(0, 0, peer_mac);
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
|
||||
if (peer) {
|
||||
struct own_svc_info *own = nan_find_own_svc(peer->own_svc_id);
|
||||
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (!s_nan_ctx.peer_creds[i].is_valid) {
|
||||
continue;
|
||||
}
|
||||
if (!nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
|
||||
continue;
|
||||
}
|
||||
if (own && memcmp(s_nan_ctx.peer_creds[i].service_hash, own->svc_hash, 6) == 0) {
|
||||
cached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cached) {
|
||||
uint8_t npk_slots = 0;
|
||||
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (s_nan_ctx.peer_creds[i].is_valid &&
|
||||
nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
|
||||
npk_slots++;
|
||||
}
|
||||
}
|
||||
if (npk_slots == 1) {
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (peer && peer->has_nik) {
|
||||
cached = true;
|
||||
}
|
||||
#endif
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return cached;
|
||||
}
|
||||
#endif
|
||||
|
||||
void esp_nan_action_start(esp_netif_t *nan_netif)
|
||||
{
|
||||
nan_set_app_default_handlers();
|
||||
@@ -1462,12 +1680,14 @@ void esp_nan_action_start(esp_netif_t *nan_netif)
|
||||
.get_nira_len = esp_nan_get_nira_len,
|
||||
.construct_nira = esp_nan_construct_nira,
|
||||
.verify_nira = esp_nan_verify_nira,
|
||||
.peer_nik_cached = nan_peer_nik_cached_cb,
|
||||
.receive_pasn = handle_auth_pasn,
|
||||
#endif
|
||||
};
|
||||
esp_nan_internal_register_callbacks(&nan_cb);
|
||||
|
||||
ESP_LOGI(TAG, "NAN Discovery started.");
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||
os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT);
|
||||
}
|
||||
|
||||
@@ -1494,6 +1714,7 @@ void esp_nan_action_stop(void)
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -1516,7 +1737,6 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* XXX: For now, NAN-USD and NAN-Sync can not coexist. */
|
||||
/* NAN-Synchronization Only */
|
||||
wifi_config_t config = {0};
|
||||
@@ -1536,10 +1756,16 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
s_nan_ctx.num_peer_creds = 0;
|
||||
memset(s_nan_ctx.peer_creds, 0, sizeof(s_nan_ctx.peer_creds));
|
||||
s_nan_ctx.use_nvs_for_caching = nan_cfg->use_nvs_for_caching;
|
||||
s_nan_ctx.nik_lifetime = 0;
|
||||
|
||||
if (nan_cfg->reset_current_nvs_creds) {
|
||||
/* Start from a clean slate: drop every credential persisted in NVS. */
|
||||
esp_wifi_nan_erase_all_creds();
|
||||
ret = esp_wifi_nan_erase_all_creds();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase NAN credentials from NVS");
|
||||
NAN_DATA_UNLOCK();
|
||||
return ret;
|
||||
}
|
||||
} else if (esp_wifi_nan_load_saved_creds(s_nan_ctx.own_nik, &s_nan_ctx.own_nik_valid,
|
||||
s_nan_ctx.peer_creds, &s_nan_ctx.num_peer_creds) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Failed to load saved NAN credentials");
|
||||
@@ -1557,7 +1783,12 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
/* Persist the freshly generated NIK only when NVS caching is enabled;
|
||||
* otherwise the identity stays ephemeral for this session. */
|
||||
if (s_nan_ctx.use_nvs_for_caching) {
|
||||
esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
|
||||
ret = esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to persist own NIK to NVS");
|
||||
NAN_DATA_UNLOCK();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Drop the cached NIRA tag; it was derived from the previous NIK. */
|
||||
@@ -1570,6 +1801,7 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_sync_config_t));
|
||||
ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_NAN, &config), TAG, "Setting NAN config failed");
|
||||
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
|
||||
if (esp_wifi_start() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Starting wifi failed");
|
||||
NAN_DATA_LOCK();
|
||||
@@ -1616,6 +1848,8 @@ esp_err_t esp_wifi_nan_sync_stop(void)
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
|
||||
/* Wait for a fresh stop event, not a stale bit from prior run. */
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||
ESP_RETURN_ON_ERROR(esp_wifi_stop(), TAG, "Stopping NAN failed");
|
||||
|
||||
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STOPPED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
@@ -1788,7 +2022,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
if (nan_check_paired_service_hash(service_id)) {
|
||||
if (cfg->pairing && nan_check_paired_service_hash(service_id)) {
|
||||
cfg->pairing->pairing_setup = false;
|
||||
}
|
||||
#endif
|
||||
@@ -1894,6 +2128,7 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
wifi_nan_subscribe_cfg_t *cfg = NULL;
|
||||
uint8_t service_id[6] = {0};
|
||||
|
||||
if (subscribe_cfg->security_reqd) {
|
||||
@@ -1940,38 +2175,64 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cfg = os_zalloc(sizeof(*cfg));
|
||||
if (!cfg) {
|
||||
ESP_LOGE(TAG, "Failed to allocate subscribe config");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(cfg, subscribe_cfg, sizeof(*cfg));
|
||||
cfg->pairing = NULL;
|
||||
if (subscribe_cfg->pairing) {
|
||||
cfg->pairing = os_malloc(sizeof(*cfg->pairing));
|
||||
if (!cfg->pairing) {
|
||||
ESP_LOGE(TAG, "Failed to copy pairing config");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(cfg->pairing, subscribe_cfg->pairing, sizeof(*cfg->pairing));
|
||||
}
|
||||
|
||||
/* Pre-claim host slot BEFORE the blob's subscribe call; see comment on
|
||||
* the publish path for the watchdog rationale. */
|
||||
|
||||
if (!nan_compute_service_id(subscribe_cfg->service_name, service_id)) {
|
||||
ESP_LOGE(TAG, "Failed to compute Service ID for %s", subscribe_cfg->service_name);
|
||||
if (!nan_compute_service_id(cfg->service_name, service_id)) {
|
||||
ESP_LOGE(TAG, "Failed to compute Service ID for %s", cfg->service_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
if (nan_check_paired_service_hash(service_id)) {
|
||||
subscribe_cfg->pairing->pairing_setup = false;
|
||||
if (cfg->pairing && nan_check_paired_service_hash(service_id)) {
|
||||
cfg->pairing->pairing_setup = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name,
|
||||
subscribe_cfg->security_cfg, subscribe_cfg->pairing)) {
|
||||
if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, cfg->service_name,
|
||||
cfg->security_cfg, cfg->pairing)) {
|
||||
ESP_LOGE(TAG, "No free service slot");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (esp_nan_internal_subscribe_service(subscribe_cfg, (uint8_t *) &sub_id, false) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", subscribe_cfg->service_name);
|
||||
nan_abort_own_svc(subscribe_cfg->service_name);
|
||||
if (esp_nan_internal_subscribe_service(cfg, (uint8_t *) &sub_id, false) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", cfg->service_name);
|
||||
nan_abort_own_svc(cfg->service_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", subscribe_cfg->service_name, sub_id);
|
||||
nan_finalize_own_svc(subscribe_cfg->service_name, (uint8_t) sub_id, false, service_id);
|
||||
ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", cfg->service_name, sub_id);
|
||||
nan_finalize_own_svc(cfg->service_name, (uint8_t) sub_id, false, service_id);
|
||||
if (cfg->pairing) {
|
||||
os_free(cfg->pairing);
|
||||
}
|
||||
os_free(cfg);
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return sub_id;
|
||||
fail:
|
||||
if (cfg) {
|
||||
if (cfg->pairing) {
|
||||
os_free(cfg->pairing);
|
||||
}
|
||||
os_free(cfg);
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
return 0;
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
|
||||
@@ -2072,6 +2333,12 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
/* Snapshot peer NMIs before cancel; clear TKs only after a successful cancel
|
||||
* so a failed attempt does not leave an active service without keys. */
|
||||
uint8_t peer_nmis[NAN_MAX_PEERS_RECORD][MACADDR_LEN];
|
||||
int peer_count = 0;
|
||||
#endif
|
||||
NAN_DATA_LOCK();
|
||||
struct own_svc_info *p_own_svc = nan_find_own_svc(service_id);
|
||||
|
||||
@@ -2080,6 +2347,27 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
{
|
||||
struct peer_svc_info *temp;
|
||||
|
||||
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
|
||||
bool dup = false;
|
||||
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
if (MACADDR_EQUAL(peer_nmis[i], temp->peer_nmi)) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dup && peer_count < NAN_MAX_PEERS_RECORD) {
|
||||
MACADDR_COPY(peer_nmis[peer_count], temp->peer_nmi);
|
||||
peer_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_own_svc->type == ESP_NAN_PUBLISH) {
|
||||
if (esp_nan_internal_publish_service(NULL, &service_id, true) == ESP_OK) {
|
||||
nan_reset_service(service_id, false);
|
||||
@@ -2102,18 +2390,24 @@ fail:
|
||||
|
||||
done:
|
||||
NAN_DATA_UNLOCK();
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
/* Cancel succeeded; now safe to wipe pairwise keys for the collected peers. */
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
nan_app_clear_one_peer_tks(peer_nmis[i]);
|
||||
}
|
||||
#endif
|
||||
return ESP_OK;
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req)
|
||||
{
|
||||
uint8_t ndp_id = 0;
|
||||
uint8_t own_bssid[6];
|
||||
ip_addr_t own_ipv6 = {0};
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(0, req->pub_id, req->peer_mac);
|
||||
|
||||
@@ -2303,6 +2597,45 @@ esp_err_t esp_wifi_nan_datapath_end(wifi_nan_datapath_end_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
esp_err_t esp_nan_app_end_peer_datapaths(const uint8_t *peer_nmi)
|
||||
{
|
||||
wifi_nan_datapath_end_req_t ndp_end[ESP_WIFI_NAN_DATAPATH_MAX_PEERS];
|
||||
int count = 0;
|
||||
esp_err_t last_err = ESP_OK;
|
||||
|
||||
if (!peer_nmi) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
|
||||
struct ndl_info *ndl = &s_nan_ctx.ndl[i];
|
||||
|
||||
if (ndl->ndp_id != 0 && MACADDR_EQUAL(ndl->peer_nmi, peer_nmi)) {
|
||||
ndp_end[count].ndp_id = ndl->ndp_id;
|
||||
MACADDR_COPY(ndp_end[count].peer_mac, peer_nmi);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
if (count == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
esp_err_t ret = esp_wifi_nan_datapath_end(&ndp_end[i]);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
last_err = ret;
|
||||
}
|
||||
}
|
||||
|
||||
return last_err;
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int *num_peer_records)
|
||||
{
|
||||
struct own_svc_info *own_svc = NULL;
|
||||
|
||||
@@ -231,6 +231,11 @@ struct own_svc_info {
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
bool nik_fup_pending;
|
||||
uint8_t nik_fup_pending_peer_nmi[MACADDR_LEN];
|
||||
/* Set when the current PASN session for @c verify_session_peer_nmi is a
|
||||
* pairing verification (re-pair). Consumed once in the key-installed
|
||||
* callback to skip the NIK follow-up exchange. */
|
||||
bool verify_session_pending;
|
||||
uint8_t verify_session_peer_nmi[MACADDR_LEN];
|
||||
#endif
|
||||
uint8_t svc_hash[6];
|
||||
};
|
||||
@@ -308,6 +313,7 @@ typedef struct {
|
||||
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS];
|
||||
uint8_t num_peer_creds;
|
||||
bool use_nvs_for_caching;
|
||||
uint32_t nik_lifetime;
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT
|
||||
struct nan_pasn_data *nan_pasn_data;
|
||||
|
||||
@@ -19,19 +19,187 @@
|
||||
#include "esp_mac.h"
|
||||
#include "esp_nan.h"
|
||||
#include "nan_i.h"
|
||||
#include "apps_private/wifi_apps_private.h"
|
||||
#include "os.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
|
||||
#include "esp_wifi_driver.h"
|
||||
|
||||
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
|
||||
#include "esp_private/esp_supp_nan.h"
|
||||
#include "apps_private/wifi_apps_private.h"
|
||||
#include "common/defs.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "nan_pairing";
|
||||
|
||||
/* Default NIK / pairing-record lifetime (also reused for paired-peer cache
|
||||
* entries) — matches the NIK Key Lifetime KDE we transmit in the post-pairing
|
||||
* Shared Key Descriptor (Wi-Fi Aware v4.0 §7.6.4.2). */
|
||||
#define NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC 86400U
|
||||
static esp_nan_app_bootstrap_cb_t s_bootstrap_cb;
|
||||
|
||||
/* Consume (clear and return) any pending verify-session for @a peer_nmi.
|
||||
* The flag lives on the own service that the peer was verified against, so it
|
||||
* survives even when no peer_svc_info exists for a MAC-randomised peer. */
|
||||
static bool nan_pairing_take_verify_session(const uint8_t *peer_nmi)
|
||||
{
|
||||
bool pending = false;
|
||||
|
||||
if (!peer_nmi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
|
||||
struct own_svc_info *own = &s_nan_ctx.own_svc[i];
|
||||
|
||||
if (own->verify_session_pending &&
|
||||
MACADDR_EQUAL(own->verify_session_peer_nmi, peer_nmi)) {
|
||||
own->verify_session_pending = false;
|
||||
pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
void esp_nan_pairing_mark_verify_session(uint8_t own_inst_id, const uint8_t *peer_nmi)
|
||||
{
|
||||
struct own_svc_info *own;
|
||||
|
||||
if (!own_inst_id || !peer_nmi) {
|
||||
return;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
own = nan_find_own_svc(own_inst_id);
|
||||
if (own) {
|
||||
own->verify_session_pending = true;
|
||||
MACADDR_COPY(own->verify_session_peer_nmi, peer_nmi);
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
|
||||
void esp_nan_pairing_clear_verify_session(const uint8_t *peer_nmi)
|
||||
{
|
||||
if (!peer_nmi) {
|
||||
return;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
|
||||
struct own_svc_info *own = &s_nan_ctx.own_svc[i];
|
||||
|
||||
if (own->verify_session_pending &&
|
||||
MACADDR_EQUAL(own->verify_session_peer_nmi, peer_nmi)) {
|
||||
own->verify_session_pending = false;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
|
||||
/* Resolve the own service instance id a peer is associated with, used to anchor
|
||||
* a proactive verify-session flag. Returns 0 when no peer record exists yet. */
|
||||
static uint8_t nan_pairing_resolve_own_inst(uint8_t peer_svc_id, const uint8_t *peer_nmi)
|
||||
{
|
||||
struct peer_svc_info *peer;
|
||||
uint8_t own_inst_id = 0;
|
||||
|
||||
if (!peer_nmi) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
peer = nan_find_peer_svc(0, peer_svc_id, (uint8_t *)peer_nmi);
|
||||
if (peer) {
|
||||
own_inst_id = peer->own_svc_id;
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return own_inst_id;
|
||||
}
|
||||
|
||||
esp_err_t esp_nan_app_set_bootstrap_cb(esp_nan_app_bootstrap_cb_t cb)
|
||||
{
|
||||
s_bootstrap_cb = cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void nan_app_bootstrap_notify(const wifi_nan_bootstrap_event_t *evt)
|
||||
{
|
||||
if (!s_bootstrap_cb) {
|
||||
ESP_LOGW(TAG, "Bootstrap frame received but no callback registered");
|
||||
return;
|
||||
}
|
||||
s_bootstrap_cb(evt);
|
||||
}
|
||||
|
||||
static bool nan_peer_cred_npk_present(const wifi_nan_peer_creds_t *c)
|
||||
{
|
||||
static const uint8_t zero_npk[ESP_WIFI_NAN_NPK_LEN] = {0};
|
||||
|
||||
if (!c || !c->is_valid) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(c->npk, zero_npk, ESP_WIFI_NAN_NPK_LEN) != 0;
|
||||
}
|
||||
|
||||
/* Resolve NPK from s_nan_ctx.peer_creds. Caller holds NAN_DATA_LOCK. */
|
||||
static const wifi_nan_peer_creds_t *nan_peer_cred_lookup(void)
|
||||
{
|
||||
const wifi_nan_peer_creds_t *only = NULL;
|
||||
uint8_t npk_slots = 0;
|
||||
|
||||
for (int idx = 0; idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; idx++) {
|
||||
const struct own_svc_info *own = &s_nan_ctx.own_svc[idx];
|
||||
|
||||
if (own->svc_id == 0) {
|
||||
continue;
|
||||
}
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (s_nan_ctx.peer_creds[i].is_valid &&
|
||||
memcmp(s_nan_ctx.peer_creds[i].service_hash, own->svc_hash, 6) == 0 &&
|
||||
nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
|
||||
return &s_nan_ctx.peer_creds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (!nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
|
||||
continue;
|
||||
}
|
||||
only = &s_nan_ctx.peer_creds[i];
|
||||
npk_slots++;
|
||||
}
|
||||
if (npk_slots == 1) {
|
||||
return only;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nan_global_peer_npk_lookup(uint8_t *npk, size_t *npk_len, int *akmp)
|
||||
{
|
||||
const wifi_nan_peer_creds_t *slot = NULL;
|
||||
|
||||
if (!npk || !npk_len || !akmp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
slot = nan_peer_cred_lookup();
|
||||
if (slot) {
|
||||
memcpy(npk, slot->npk, ESP_WIFI_NAN_NPK_LEN);
|
||||
*npk_len = ESP_WIFI_NAN_NPK_LEN;
|
||||
/* Wi-Fi Aware pairing: PASN frames use SAE as the single base AKM. */
|
||||
*akmp = WPA_KEY_MGMT_SAE;
|
||||
NAN_DATA_UNLOCK();
|
||||
return 0;
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct nan_pasn_data *esp_nan_app_get_pasn_data(void)
|
||||
{
|
||||
@@ -43,13 +211,6 @@ void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd)
|
||||
s_nan_ctx.nan_pasn_data = pd;
|
||||
}
|
||||
|
||||
static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi,
|
||||
uint8_t role,
|
||||
uint8_t ndp_csid,
|
||||
const uint8_t *nd_pmk,
|
||||
size_t nd_pmk_len,
|
||||
uint32_t nik_lifetime_sec);
|
||||
|
||||
bool nan_pairing_validate_publish_bootstrapping(uint16_t bootstrapping_methods)
|
||||
{
|
||||
if (!bootstrapping_methods) {
|
||||
@@ -79,34 +240,42 @@ uint16_t nan_app_parse_npba_from_publish(const struct nan_cb_npba_t *npba)
|
||||
void nan_app_bootstrap_indication(uint8_t peer_svc_id, uint8_t pub_id,
|
||||
uint8_t peer_nmi[6], uint16_t selected_method)
|
||||
{
|
||||
wifi_nan_bootstrap_event_t evt = {0};
|
||||
|
||||
ESP_LOGI(TAG, "Pairing Bootstrapping Request from "MACSTR" [pub_id=%d, method=0x%x]",
|
||||
MAC2STR(peer_nmi), pub_id, selected_method);
|
||||
|
||||
wifi_event_nan_bootstrap_indication_t evt = {0};
|
||||
evt.type = WIFI_NAN_NPBA_TYPE_REQUEST;
|
||||
evt.peer_svc_id = peer_svc_id;
|
||||
evt.own_svc_id = pub_id;
|
||||
MACADDR_COPY(evt.peer_nmi, peer_nmi);
|
||||
evt.selected_method = selected_method;
|
||||
evt.methods = selected_method;
|
||||
|
||||
nan_app_post_event(WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, &evt, sizeof(evt));
|
||||
nan_app_bootstrap_notify(&evt);
|
||||
}
|
||||
|
||||
void nan_app_bootstrap_completed(uint8_t status, uint8_t peer_svc_id, uint8_t sub_id,
|
||||
uint8_t peer_nmi[6], uint16_t matched_method,
|
||||
uint8_t reason_code)
|
||||
{
|
||||
wifi_nan_bootstrap_event_t evt = {0};
|
||||
|
||||
ESP_LOGI(TAG, "Pairing Bootstrapping Response from "MACSTR" [sub_id=%d, status=%d, method=0x%x]",
|
||||
MAC2STR(peer_nmi), sub_id, status, matched_method);
|
||||
|
||||
wifi_event_nan_bootstrap_complete_t evt = {0};
|
||||
if (peer_nmi == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
evt.type = WIFI_NAN_NPBA_TYPE_RESPONSE;
|
||||
evt.status = status;
|
||||
evt.peer_svc_id = peer_svc_id;
|
||||
evt.own_svc_id = sub_id;
|
||||
MACADDR_COPY(evt.peer_nmi, peer_nmi);
|
||||
evt.matched_method = matched_method;
|
||||
evt.methods = matched_method;
|
||||
evt.reason_code = reason_code;
|
||||
|
||||
nan_app_post_event(WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, &evt, sizeof(evt));
|
||||
nan_app_bootstrap_notify(&evt);
|
||||
}
|
||||
|
||||
bool nan_app_parse_npba_from_receive(uint8_t own_svc_id, uint8_t peer_svc_id,
|
||||
@@ -243,48 +412,6 @@ esp_err_t esp_wifi_nan_bootstrap_response(wifi_nan_pairing_bootstrapping_resp_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg)
|
||||
{
|
||||
if (!cfg) {
|
||||
ESP_LOGE(TAG, "Pairing config NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (cfg->cred.pincode != UINT32_MAX &&
|
||||
cfg->cred.pincode > NAN_PAIRING_PINCODE_MAX) {
|
||||
ESP_LOGE(TAG, "Invalid pincode %u (valid range %u..%u or UINT32_MAX for default)",
|
||||
cfg->cred.pincode, NAN_PAIRING_PINCODE_MIN, NAN_PAIRING_PINCODE_MAX);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int ret;
|
||||
|
||||
switch (cfg->self_role) {
|
||||
case NAN_PAIRING_ROLE_RESPONDER:
|
||||
ret = esp_nan_supp_pasn_responder_init(cfg->peer_nmi, cfg->cred.pincode,
|
||||
NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC,
|
||||
nan_pairing_key_installed_cb);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "NAN PASN responder init failed for "MACSTR, MAC2STR(cfg->peer_nmi));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
case NAN_PAIRING_ROLE_INITIATOR:
|
||||
ret = esp_nan_supp_pasn_initiator_auth(cfg->peer_nmi, cfg->cred.pincode,
|
||||
NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC,
|
||||
nan_pairing_key_installed_cb);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "NAN PASN initiator auth failed for "MACSTR, MAC2STR(cfg->peer_nmi));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid pairing role %d", cfg->self_role);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* NIRA: ID(1) + Len(2) + CipherVersion(1) + Nonce(8) + Tag(8) = 20 */
|
||||
#define NAN_ATTR_ID_IDENTITY_RESOLUTION 0x2B
|
||||
#define NAN_NIRA_NONCE_LEN 8
|
||||
@@ -299,47 +426,6 @@ uint32_t esp_nan_get_nira_len(void)
|
||||
return NAN_NIRA_ATTR_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a NIRA tag for cipher version 0 (Wi-Fi Aware v4.0):
|
||||
* Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI || Nonce))
|
||||
*
|
||||
* Ported from hostap @c nan_crypto_derive_nira_tag (src/nan/nan_crypto.c),
|
||||
* adapted to the ESP-IDF crypto trampoline.
|
||||
*/
|
||||
static int nan_pairing_derive_nira_tag(const uint8_t nik[NAN_PASN_NIK_LEN],
|
||||
const uint8_t nmi_addr[ETH_ALEN],
|
||||
const uint8_t nira_nonce[NAN_NIRA_NONCE_LEN],
|
||||
uint8_t tag_out[NAN_NIRA_TAG_LEN])
|
||||
{
|
||||
const unsigned char *addr[3];
|
||||
int len_arr[3];
|
||||
uint8_t digest[32];
|
||||
|
||||
if (!nik || !nmi_addr || !nira_nonce || !tag_out) {
|
||||
return -1;
|
||||
}
|
||||
if (!g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr[0] = (const unsigned char *)NAN_NIRA_STR;
|
||||
len_arr[0] = NAN_NIRA_STR_LEN;
|
||||
addr[1] = nmi_addr;
|
||||
len_arr[1] = ETH_ALEN;
|
||||
addr[2] = nira_nonce;
|
||||
len_arr[2] = NAN_NIRA_NONCE_LEN;
|
||||
|
||||
if (g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector(nik, NAN_PASN_NIK_LEN,
|
||||
3, addr, len_arr,
|
||||
digest) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(tag_out, digest, NAN_NIRA_TAG_LEN);
|
||||
memset(digest, 0, sizeof(digest));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int esp_nan_construct_nira(uint8_t *frm)
|
||||
{
|
||||
const uint8_t *nonce;
|
||||
@@ -356,11 +442,18 @@ int esp_nan_construct_nira(uint8_t *frm)
|
||||
tag = s_nan_ctx.cached_nira_tag;
|
||||
} else {
|
||||
uint8_t own_nmi[MACADDR_LEN];
|
||||
const unsigned char *addr[3];
|
||||
int len_arr[3];
|
||||
uint8_t digest[32];
|
||||
|
||||
if (!s_nan_ctx.own_nik_valid) {
|
||||
ESP_LOGW(TAG, "NIRA: own NIK is not available");
|
||||
return 0;
|
||||
}
|
||||
if (!g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector) {
|
||||
ESP_LOGE(TAG, "NIRA: hmac_sha256_vector not registered");
|
||||
return 0;
|
||||
}
|
||||
if (esp_wifi_get_mac(WIFI_IF_NAN, own_nmi) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NIRA: failed to read NAN NMI");
|
||||
return 0;
|
||||
@@ -369,12 +462,23 @@ int esp_nan_construct_nira(uint8_t *frm)
|
||||
ESP_LOGE(TAG, "NIRA: failed to generate nonce");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI || Nonce)) */
|
||||
if (nan_pairing_derive_nira_tag(s_nan_ctx.own_nik, own_nmi,
|
||||
fresh_nonce, fresh_tag) != 0) {
|
||||
addr[0] = (const unsigned char *)NAN_NIRA_STR;
|
||||
len_arr[0] = NAN_NIRA_STR_LEN;
|
||||
addr[1] = own_nmi;
|
||||
len_arr[1] = MACADDR_LEN;
|
||||
addr[2] = fresh_nonce;
|
||||
len_arr[2] = NAN_NIRA_NONCE_LEN;
|
||||
if (g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector(s_nan_ctx.own_nik,
|
||||
ESP_WIFI_NAN_NIK_LEN,
|
||||
3, addr, len_arr,
|
||||
digest) != 0) {
|
||||
ESP_LOGE(TAG, "NIRA: tag derivation failed");
|
||||
return 0;
|
||||
}
|
||||
memcpy(fresh_tag, digest, NAN_NIRA_TAG_LEN);
|
||||
memset(digest, 0, sizeof(digest));
|
||||
|
||||
memcpy(s_nan_ctx.cached_nira_nonce, fresh_nonce, NAN_NIRA_NONCE_LEN);
|
||||
memcpy(s_nan_ctx.cached_nira_tag, fresh_tag, NAN_NIRA_TAG_LEN);
|
||||
@@ -405,7 +509,6 @@ int esp_nan_construct_nira(uint8_t *frm)
|
||||
#define NAN_PASN_KDE_OUI_TYPE_NIK 36
|
||||
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
|
||||
#define NAN_PASN_KEY_LIFETIME_NIK_BIT BIT(3)
|
||||
/* NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC is defined at file scope above. */
|
||||
#define NAN_ATTR_ID_SHARED_KEY_DESC 0x24
|
||||
#define NAN_PAIRING_NIK_FUP_TIMEOUT_SEC 2
|
||||
|
||||
@@ -528,6 +631,47 @@ static size_t nan_pairing_build_srv_ssi(uint8_t *buf, size_t buf_len)
|
||||
return (size_t)(p - buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a NIRA tag for cipher version 0 (Wi-Fi Aware v4.0):
|
||||
* Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI || Nonce))
|
||||
*
|
||||
* Ported from hostap @c nan_crypto_derive_nira_tag (src/nan/nan_crypto.c),
|
||||
* adapted to the ESP-IDF crypto trampoline.
|
||||
*/
|
||||
static int nan_pairing_derive_nira_tag(const uint8_t nik[NAN_PASN_NIK_LEN],
|
||||
const uint8_t nmi_addr[ETH_ALEN],
|
||||
const uint8_t nira_nonce[NAN_NIRA_NONCE_LEN],
|
||||
uint8_t tag_out[NAN_NIRA_TAG_LEN])
|
||||
{
|
||||
const unsigned char *addr[3];
|
||||
int len_arr[3];
|
||||
uint8_t digest[32];
|
||||
|
||||
if (!nik || !nmi_addr || !nira_nonce || !tag_out) {
|
||||
return -1;
|
||||
}
|
||||
if (!g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr[0] = (const unsigned char *)NAN_NIRA_STR;
|
||||
len_arr[0] = NAN_NIRA_STR_LEN;
|
||||
addr[1] = nmi_addr;
|
||||
len_arr[1] = ETH_ALEN;
|
||||
addr[2] = nira_nonce;
|
||||
len_arr[2] = NAN_NIRA_NONCE_LEN;
|
||||
|
||||
if (g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector(nik, NAN_PASN_NIK_LEN,
|
||||
3, addr, len_arr,
|
||||
digest) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(tag_out, digest, NAN_NIRA_TAG_LEN);
|
||||
memset(digest, 0, sizeof(digest));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t nan_pairing_build_plain_key_data(uint8_t *buf, size_t buf_len,
|
||||
const uint8_t nik[NAN_PASN_NIK_LEN])
|
||||
{
|
||||
@@ -559,7 +703,7 @@ static size_t nan_pairing_build_plain_key_data(uint8_t *buf, size_t buf_len,
|
||||
buf[pos++] = NAN_PASN_KDE_OUI_TYPE_LIFETIME;
|
||||
WPA_PUT_LE16(&buf[pos], NAN_PASN_KEY_LIFETIME_NIK_BIT);
|
||||
pos += 2;
|
||||
WPA_PUT_BE32(&buf[pos], NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC);
|
||||
WPA_PUT_BE32(&buf[pos], s_nan_ctx.nik_lifetime);
|
||||
pos += 4;
|
||||
|
||||
/*
|
||||
@@ -567,16 +711,14 @@ static size_t nan_pairing_build_plain_key_data(uint8_t *buf, size_t buf_len,
|
||||
* 802.11-2020 §12.7.2 specifies Key Data padding as a single 0xDD byte
|
||||
* followed by zeros (not a sequence of well-formed vendor IEs).
|
||||
*/
|
||||
{
|
||||
size_t pad = (8 - (pos % 8)) % 8;
|
||||
if (pad) {
|
||||
if (buf_len - pos < pad) {
|
||||
return 0;
|
||||
}
|
||||
buf[pos++] = 0xDD;
|
||||
while (--pad) {
|
||||
buf[pos++] = 0x00;
|
||||
}
|
||||
size_t pad = (8 - (pos % 8)) % 8;
|
||||
if (pad) {
|
||||
if (buf_len - pos < pad) {
|
||||
return 0;
|
||||
}
|
||||
buf[pos++] = 0xDD;
|
||||
while (--pad) {
|
||||
buf[pos++] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,9 +825,8 @@ static esp_err_t nan_app_send_pairing_followup(uint8_t svc_id, uint8_t peer_svc_
|
||||
|
||||
/* NIRA proves possession of the NIK we just wrapped above; iPhone uses
|
||||
* it to bind the NIK to the sender and won't commit the pairing record
|
||||
* without it. esp_nan_construct_nira() reuses the same cached nonce/tag we
|
||||
* advertise in sync discovery. */
|
||||
nira_len = (size_t)esp_nan_construct_nira(nira_attr);
|
||||
* without it. */
|
||||
nira_len = esp_nan_construct_nira(nira_attr);
|
||||
if (nira_len == 0) {
|
||||
ESP_LOGW(TAG, "Pairing follow-up: NIRA attribute build failed");
|
||||
return ESP_FAIL;
|
||||
@@ -713,113 +854,12 @@ static void nan_app_send_pairing_followup_eloop(void *eloop_data, void *user_dat
|
||||
os_free(ctx);
|
||||
}
|
||||
|
||||
static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi,
|
||||
uint8_t role,
|
||||
uint8_t ndp_csid,
|
||||
const uint8_t *nd_pmk,
|
||||
size_t nd_pmk_len,
|
||||
uint32_t nik_lifetime_sec)
|
||||
{
|
||||
if (!peer_nmi) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t lifetime_sec = nik_lifetime_sec ?
|
||||
nik_lifetime_sec : NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC;
|
||||
|
||||
/* Cache ND-PMK for future paired NDPs (Wi-Fi Aware v4.0 §7.6.4.2). */
|
||||
if (ndp_csid && nd_pmk && nd_pmk_len == ESP_WIFI_NAN_NDP_PMK_LEN) {
|
||||
(void)nan_app_register_paired_peer(peer_nmi, role, ndp_csid,
|
||||
nd_pmk, nd_pmk_len,
|
||||
lifetime_sec);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Pairing complete for " MACSTR
|
||||
": ND-PMK unavailable (csid=%u nd_pmk_len=%u); "
|
||||
"paired-peer cache not updated",
|
||||
MAC2STR(peer_nmi), ndp_csid, (unsigned)nd_pmk_len);
|
||||
}
|
||||
|
||||
struct peer_svc_info *peer = nan_find_peer_svc(0, 0, (uint8_t *)peer_nmi);
|
||||
struct own_svc_info *own = NULL;
|
||||
|
||||
if (peer) {
|
||||
own = nan_find_own_svc(peer->own_svc_id);
|
||||
}
|
||||
|
||||
if (own) {
|
||||
if (!own->pairing.npk_nik_caching) {
|
||||
wifi_event_nan_pairing_complete_t evt = {0};
|
||||
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
|
||||
evt.reason_code = 0;
|
||||
MACADDR_COPY(evt.peer_nmi, peer_nmi);
|
||||
esp_nan_complete_pairing(own->svc_id, peer->svc_id);
|
||||
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (role == NAN_ROLE_PAIRING_INITIATOR) {
|
||||
struct nan_pairing_fup_ctx *ctx = os_zalloc(sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
ESP_LOGW(TAG, "Pairing key installed: failed to alloc fup ctx for " MACSTR,
|
||||
MAC2STR(peer_nmi));
|
||||
return;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
|
||||
if (peer) {
|
||||
ctx->svc_id = peer->own_svc_id;
|
||||
ctx->peer_svc_id = peer->svc_id;
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
/* Without a peer service entry the follow-up would carry zero service
|
||||
* IDs; drop it rather than send an invalid frame. */
|
||||
if (!ctx->svc_id || !ctx->peer_svc_id) {
|
||||
ESP_LOGW(TAG, "Pairing key installed: peer service not found for " MACSTR
|
||||
", skipping initiator follow-up", MAC2STR(peer_nmi));
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
MACADDR_COPY(ctx->peer_mac, peer_nmi);
|
||||
ctx->shared_key_attr_len = 0;
|
||||
|
||||
own = nan_find_own_svc(ctx->svc_id);
|
||||
if (own) {
|
||||
nan_pairing_arm_pending(own, peer_nmi);
|
||||
}
|
||||
|
||||
if (eloop_register_timeout(0, 0, nan_app_send_pairing_followup_eloop, NULL, ctx) != 0) {
|
||||
ESP_LOGW(TAG, "Pairing key installed: failed to schedule initiator follow-up");
|
||||
if (own) {
|
||||
nan_pairing_cancel_svc_pending(own);
|
||||
}
|
||||
os_free(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == NAN_ROLE_PAIRING_RESPONDER) {
|
||||
NAN_DATA_LOCK();
|
||||
|
||||
if (peer) {
|
||||
own = nan_find_own_svc(peer->own_svc_id);
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
if (own) {
|
||||
nan_pairing_arm_pending(own, peer_nmi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert or refresh a (peer NIK, NPK) entry in the in-RAM credential cache used
|
||||
* for NIRA identity resolution. Caller holds NAN_DATA_LOCK. A @a npk of NULL
|
||||
* stores a zeroed key. When the cache is full the oldest entry (slot 0) is
|
||||
* reused. */
|
||||
static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *npk, uint8_t service_hash[6])
|
||||
* stores a zeroed key; NULL @a service_hash stores zeros. When the cache is
|
||||
* full the oldest entry (slot 0) is reused. */
|
||||
static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *npk,
|
||||
const uint8_t service_hash[6])
|
||||
{
|
||||
wifi_nan_peer_creds_t *slot = NULL;
|
||||
|
||||
@@ -840,7 +880,11 @@ static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *np
|
||||
}
|
||||
|
||||
memcpy(slot->peer_nik, peer_nik, ESP_WIFI_NAN_NIK_LEN);
|
||||
memcpy(slot->service_hash, service_hash, 6);
|
||||
if (service_hash) {
|
||||
memcpy(slot->service_hash, service_hash, 6);
|
||||
} else {
|
||||
memset(slot->service_hash, 0, sizeof(slot->service_hash));
|
||||
}
|
||||
if (npk) {
|
||||
memcpy(slot->npk, npk, ESP_WIFI_NAN_NPK_LEN);
|
||||
} else {
|
||||
@@ -849,6 +893,212 @@ static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *np
|
||||
slot->is_valid = true;
|
||||
}
|
||||
|
||||
static void nan_pairing_post_confirm(const uint8_t *peer_nmi)
|
||||
{
|
||||
wifi_event_nan_pairing_complete_t evt = {0};
|
||||
|
||||
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
|
||||
MACADDR_COPY(evt.peer_nmi, peer_nmi);
|
||||
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
|
||||
}
|
||||
|
||||
static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi,
|
||||
uint8_t role,
|
||||
uint8_t pairing_verification,
|
||||
uint8_t ndp_csid,
|
||||
const uint8_t *nd_pmk,
|
||||
size_t nd_pmk_len,
|
||||
uint32_t nik_lifetime_sec)
|
||||
{
|
||||
(void)pairing_verification;
|
||||
|
||||
if (!peer_nmi) {
|
||||
return;
|
||||
}
|
||||
|
||||
const struct nan_pasn_key_material *keys = nan_pasn_get_saved_keys();
|
||||
uint8_t peer_remote_svc_id = 0;
|
||||
bool npk_nik_caching = true;
|
||||
uint8_t own_svc_id = 0;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
struct peer_svc_info *peer = nan_find_peer_svc(0, 0, (uint8_t *)peer_nmi);
|
||||
if (peer) {
|
||||
struct own_svc_info *own = nan_find_own_svc(peer->own_svc_id);
|
||||
|
||||
peer_remote_svc_id = peer->svc_id;
|
||||
own_svc_id = peer->own_svc_id;
|
||||
if (own) {
|
||||
npk_nik_caching = own->pairing.npk_nik_caching;
|
||||
}
|
||||
if (keys && keys->pmk_len && keys->pmk_len <= ESP_WIFI_NAN_NPK_LEN &&
|
||||
peer->has_nik) {
|
||||
uint8_t npk_tmp[ESP_WIFI_NAN_NPK_LEN] = {0};
|
||||
|
||||
memcpy(npk_tmp, keys->pmk, keys->pmk_len);
|
||||
nan_app_update_peer_creds(peer->peer_nik, npk_tmp,
|
||||
own ? own->svc_hash : NULL);
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
uint32_t lifetime_sec = nik_lifetime_sec;
|
||||
|
||||
/* Cache ND-PMK for future paired NDPs (Wi-Fi Aware v4.0 §7.6.4.2). */
|
||||
if (ndp_csid && nd_pmk && nd_pmk_len == ESP_WIFI_NAN_NDP_PMK_LEN) {
|
||||
(void)nan_app_register_paired_peer(peer_nmi, role, ndp_csid,
|
||||
nd_pmk, nd_pmk_len,
|
||||
lifetime_sec);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Pairing complete for " MACSTR
|
||||
": ND-PMK unavailable (csid=%u nd_pmk_len=%u); "
|
||||
"paired-peer cache not updated",
|
||||
MAC2STR(peer_nmi), ndp_csid, (unsigned)nd_pmk_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-pair verification has no follow-up exchange (the initiator
|
||||
* goes straight to NDP). Post PAIRING_CONFIRM here so the application
|
||||
* is notified that PASN re-pairing is complete.
|
||||
*/
|
||||
if (nan_pairing_take_verify_session(peer_nmi) &&
|
||||
(role == NAN_ROLE_PAIRING_RESPONDER || role == NAN_ROLE_PAIRING_INITIATOR)) {
|
||||
/* Re-verification: no follow-up ping-pong; notify app directly. */
|
||||
esp_nan_complete_pairing(own_svc_id, peer_remote_svc_id);
|
||||
nan_pairing_post_confirm(peer_nmi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (own_svc_id && !npk_nik_caching) {
|
||||
wifi_event_nan_pairing_complete_t evt = {0};
|
||||
|
||||
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
|
||||
evt.reason_code = 0;
|
||||
MACADDR_COPY(evt.peer_nmi, peer_nmi);
|
||||
esp_nan_complete_pairing(own_svc_id, peer_remote_svc_id);
|
||||
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == NAN_ROLE_PAIRING_INITIATOR) {
|
||||
|
||||
struct nan_pairing_fup_ctx *ctx = os_zalloc(sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
ESP_LOGW(TAG, "Pairing key installed: failed to alloc fup ctx for " MACSTR,
|
||||
MAC2STR(peer_nmi));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->svc_id = own_svc_id;
|
||||
ctx->peer_svc_id = peer_remote_svc_id;
|
||||
|
||||
/* Without a peer service entry the follow-up would carry zero service
|
||||
* IDs; drop it rather than send an invalid frame. */
|
||||
if (!ctx->svc_id || !ctx->peer_svc_id) {
|
||||
ESP_LOGW(TAG, "Pairing key installed: peer service not found for " MACSTR
|
||||
", skipping initiator follow-up", MAC2STR(peer_nmi));
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
MACADDR_COPY(ctx->peer_mac, peer_nmi);
|
||||
ctx->shared_key_attr_len = 0;
|
||||
|
||||
bool armed_pending = false;
|
||||
NAN_DATA_LOCK();
|
||||
struct own_svc_info *own = nan_find_own_svc(ctx->svc_id);
|
||||
if (own) {
|
||||
nan_pairing_arm_pending(own, peer_nmi);
|
||||
armed_pending = true;
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
if (eloop_register_timeout(0, 0, nan_app_send_pairing_followup_eloop, NULL, ctx) != 0) {
|
||||
ESP_LOGW(TAG, "Pairing key installed: failed to schedule initiator follow-up");
|
||||
if (armed_pending) {
|
||||
NAN_DATA_LOCK();
|
||||
own = nan_find_own_svc(ctx->svc_id);
|
||||
if (own) {
|
||||
nan_pairing_cancel_svc_pending(own);
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
os_free(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (role == NAN_ROLE_PAIRING_RESPONDER) {
|
||||
NAN_DATA_LOCK();
|
||||
struct own_svc_info *own = nan_find_own_svc(own_svc_id);
|
||||
if (own) {
|
||||
nan_pairing_arm_pending(own, peer_nmi);
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg)
|
||||
{
|
||||
if (!cfg) {
|
||||
ESP_LOGE(TAG, "Pairing config NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
|
||||
if (!cfg->pairing_verification &&
|
||||
cfg->cred.pincode != UINT32_MAX &&
|
||||
cfg->cred.pincode > NAN_PAIRING_PINCODE_MAX) {
|
||||
ESP_LOGE(TAG, "Invalid pincode %u (valid range %u..%u or UINT32_MAX for default)",
|
||||
cfg->cred.pincode, NAN_PAIRING_PINCODE_MIN, NAN_PAIRING_PINCODE_MAX);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ret;
|
||||
|
||||
switch (cfg->self_role) {
|
||||
case NAN_PAIRING_ROLE_RESPONDER:
|
||||
/* Verify-session is marked on Auth1 RX after NIRA OK in
|
||||
* handle_auth_pasn(); initiator-side verify marks in pairing_start(). */
|
||||
if (!cfg->pairing_verification) {
|
||||
esp_nan_pairing_clear_verify_session(cfg->peer_nmi);
|
||||
}
|
||||
ret = esp_nan_supp_pasn_responder_init(cfg->peer_nmi, cfg->cred.pincode,
|
||||
0,
|
||||
nan_pairing_key_installed_cb);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "NAN PASN responder init failed for "MACSTR, MAC2STR(cfg->peer_nmi));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
case NAN_PAIRING_ROLE_INITIATOR:
|
||||
if (cfg->pairing_verification) {
|
||||
esp_nan_pairing_mark_verify_session(
|
||||
nan_pairing_resolve_own_inst(cfg->peer_svc_id, cfg->peer_nmi),
|
||||
cfg->peer_nmi);
|
||||
ret = esp_nan_supp_pasn_initiator_verify(cfg->peer_nmi,
|
||||
nan_pairing_key_installed_cb);
|
||||
} else {
|
||||
esp_nan_pairing_clear_verify_session(cfg->peer_nmi);
|
||||
ret = esp_nan_supp_pasn_initiator_auth(cfg->peer_nmi, cfg->cred.pincode,
|
||||
0,
|
||||
nan_pairing_key_installed_cb);
|
||||
}
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "NAN PASN initiator %s failed for "MACSTR,
|
||||
cfg->pairing_verification ? "verify" : "auth",
|
||||
MAC2STR(cfg->peer_nmi));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid pairing role %d", cfg->self_role);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
const uint8_t *peer_mac,
|
||||
const uint8_t *shared_key_attr,
|
||||
@@ -870,9 +1120,7 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
if (shared_key_attr[0] != NAN_ATTR_ID_SHARED_KEY_DESC) {
|
||||
return;
|
||||
}
|
||||
const uint16_t *attr_body_len_field = (const uint16_t *)&shared_key_attr[1];
|
||||
|
||||
attr_body_len = *attr_body_len_field;
|
||||
attr_body_len = WPA_GET_LE16(&shared_key_attr[1]);
|
||||
if (attr_body_len > shared_key_attr_buf_len - 3) {
|
||||
ESP_LOGW(TAG, "Pairing follow-up: truncated Shared Key Descriptor (body=%zu, avail=%zu)",
|
||||
attr_body_len, shared_key_attr_buf_len);
|
||||
@@ -898,20 +1146,31 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc_exact(svc_id, peer_svc_id, peer_mac);
|
||||
if (!p_peer_svc) {
|
||||
/* NDP may have created a peer entry with peer_svc_id=0 before follow-up. */
|
||||
p_peer_svc = nan_find_peer_svc(svc_id, 0, (uint8_t *)peer_mac);
|
||||
}
|
||||
if (p_peer_svc) {
|
||||
already_had_nik = p_peer_svc->has_nik;
|
||||
memcpy(p_peer_svc->peer_nik, nik, NAN_APP_PEER_NIK_LEN);
|
||||
p_peer_svc->peer_nik_cipher_ver = cipher_ver;
|
||||
p_peer_svc->peer_nik_lifetime_sec = lifetime_sec;
|
||||
p_peer_svc->has_nik = true;
|
||||
s_nan_ctx.nik_lifetime = lifetime_sec;
|
||||
ESP_LOGI(TAG, "Stored peer NIK from " MACSTR " (cipher_ver=%u, lifetime=%u s)",
|
||||
MAC2STR(peer_mac), cipher_ver, lifetime_sec);
|
||||
|
||||
/* Refresh the NIRA credential cache with this peer's NIK and, if the
|
||||
* pairing record is available, its NPK. */
|
||||
/* Refresh peer_creds (and NVS when enabled) with peer NIK and PASN NPK. */
|
||||
const struct nan_paired_peer *paired = nan_app_find_paired_peer(peer_mac);
|
||||
if (paired) {
|
||||
const struct nan_pasn_key_material *keys = nan_pasn_get_saved_keys();
|
||||
const uint8_t *npk_src = NULL;
|
||||
|
||||
if (keys && keys->pmk_len && keys->pmk_len <= ESP_WIFI_NAN_NPK_LEN) {
|
||||
memcpy(persist_npk, keys->pmk, keys->pmk_len);
|
||||
npk_src = persist_npk;
|
||||
} else if (paired) {
|
||||
memcpy(persist_npk, paired->nd_pmk, ESP_WIFI_NAN_NPK_LEN);
|
||||
npk_src = persist_npk;
|
||||
}
|
||||
|
||||
own = nan_find_own_svc(p_peer_svc->own_svc_id);
|
||||
@@ -920,8 +1179,10 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
pairing_completed = true;
|
||||
}
|
||||
|
||||
nan_app_update_peer_creds(nik, paired ? paired->nd_pmk : NULL, own ? own->svc_hash : NULL);
|
||||
persist_creds = s_nan_ctx.use_nvs_for_caching;
|
||||
nan_app_update_peer_creds(nik, npk_src, own ? own->svc_hash : NULL);
|
||||
if (s_nan_ctx.nik_lifetime == 0 && s_nan_ctx.use_nvs_for_caching) {
|
||||
persist_creds = true;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
@@ -929,18 +1190,18 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
if (persist_creds) {
|
||||
esp_wifi_nan_save_creds_for_peer(nik, persist_npk, own ? own->svc_hash : NULL);
|
||||
}
|
||||
|
||||
/* Invoke blocking calls outside NAN_DATA_LOCK to avoid deadlock. */
|
||||
if (pairing_completed) {
|
||||
wifi_event_nan_pairing_complete_t evt = {0};
|
||||
|
||||
if (own) {
|
||||
nan_pairing_cancel_svc_pending(own);
|
||||
esp_nan_complete_pairing(own->svc_id, peer_svc_id);
|
||||
}
|
||||
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
|
||||
evt.reason_code = 0;
|
||||
MACADDR_COPY(evt.peer_nmi, peer_mac);
|
||||
esp_nan_complete_pairing(p_peer_svc ? p_peer_svc->own_svc_id : 0,
|
||||
p_peer_svc ? p_peer_svc->svc_id : peer_svc_id);
|
||||
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
|
||||
}
|
||||
|
||||
@@ -973,13 +1234,22 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
}
|
||||
}
|
||||
|
||||
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len)
|
||||
/* Core NIRA verification. When @a own_inst_id is non-NULL it also resolves the
|
||||
* matched identity to a live own service instance id (0 if the verifying NIK
|
||||
* does not map to an active local service), used to anchor a verify-session
|
||||
* flag. The bool result reflects identity match only. */
|
||||
static bool nan_verify_nira_internal(uint8_t *peer_mac, uint8_t *nira_attr,
|
||||
uint16_t nira_attr_len, uint8_t *own_inst_id)
|
||||
{
|
||||
uint8_t expected_tag[NAN_NIRA_TAG_LEN];
|
||||
const uint8_t *nonce;
|
||||
const uint8_t *received_tag;
|
||||
bool match = false;
|
||||
|
||||
if (own_inst_id) {
|
||||
*own_inst_id = 0;
|
||||
}
|
||||
|
||||
if (!peer_mac || !nira_attr) {
|
||||
return false;
|
||||
}
|
||||
@@ -1000,13 +1270,24 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
|
||||
if (!s_nan_ctx.peer_creds[i].is_valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nan_pairing_derive_nira_tag(s_nan_ctx.peer_creds[i].peer_nik, peer_mac,
|
||||
nonce, expected_tag) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (os_memcmp_const(expected_tag, received_tag, NAN_NIRA_TAG_LEN) == 0) {
|
||||
match = true;
|
||||
if (own_inst_id) {
|
||||
for (int idx = 0; idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; idx++) {
|
||||
struct own_svc_info *own = &s_nan_ctx.own_svc[idx];
|
||||
|
||||
if (own->svc_id &&
|
||||
memcmp(own->svc_hash,
|
||||
s_nan_ctx.peer_creds[i].service_hash, 6) == 0) {
|
||||
*own_inst_id = own->svc_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1015,9 +1296,27 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
|
||||
if (match) {
|
||||
ESP_LOGD(TAG, "NIRA verify: OK for "MACSTR, MAC2STR(peer_mac));
|
||||
} else {
|
||||
ESP_LOGD(TAG, "NIRA verify: no matching NIK for "MACSTR, MAC2STR(peer_mac));
|
||||
ESP_LOGW(TAG, "NIRA verify: no matching NIK for "MACSTR, MAC2STR(peer_mac));
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len)
|
||||
{
|
||||
return nan_verify_nira_internal(peer_mac, nira_attr, nira_attr_len, NULL);
|
||||
}
|
||||
|
||||
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
|
||||
uint16_t nira_attr_len, uint8_t *own_inst_id)
|
||||
{
|
||||
return nan_verify_nira_internal(peer_mac, nira_attr, nira_attr_len, own_inst_id);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
|
||||
esp_nan_pairing_key_installed_cb_t esp_nan_pairing_get_key_installed_cb(void)
|
||||
{
|
||||
return nan_pairing_key_installed_cb;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_private/wifi.h"
|
||||
#include "esp_log.h"
|
||||
@@ -315,30 +316,70 @@ static int nan_ndp_ptk_derive(const uint8_t *pmk, const uint8_t *i_addr, const u
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool nan_mac_is_zero(const uint8_t mac[6])
|
||||
{
|
||||
static const uint8_t zero[6] = {0};
|
||||
|
||||
return memcmp(mac, zero, 6) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve IAddr/RAddr for NDP PTK derivation. When NDPE has not yet assigned
|
||||
* an NDI, fall back to the peer NMI so both sides derive the same PTK.
|
||||
* Returns 0 on success, -1 if our NAN MAC cannot be retrieved.
|
||||
*/
|
||||
static int nan_ndp_ptk_addrs(const struct ndl_info *ndl, bool responder,
|
||||
uint8_t i_addr[6], uint8_t r_addr[6])
|
||||
{
|
||||
uint8_t our_mac[6];
|
||||
|
||||
if (esp_wifi_get_mac(WIFI_IF_NAN, our_mac) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "nan_ndp_ptk_addrs: get our MAC failed");
|
||||
return -1;
|
||||
}
|
||||
if (responder) {
|
||||
if (nan_mac_is_zero(ndl->peer_ndi)) {
|
||||
memcpy(i_addr, ndl->peer_nmi, 6);
|
||||
} else {
|
||||
memcpy(i_addr, ndl->peer_ndi, 6);
|
||||
}
|
||||
memcpy(r_addr, our_mac, 6);
|
||||
} else {
|
||||
memcpy(i_addr, our_mac, 6);
|
||||
if (nan_mac_is_zero(ndl->peer_ndi)) {
|
||||
memcpy(r_addr, ndl->peer_nmi, 6);
|
||||
} else {
|
||||
memcpy(r_addr, ndl->peer_ndi, 6);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lazy initiator PTK derivation. No-op if ndl->ptk_set is already 1.
|
||||
* Caller MUST hold NAN_DATA_LOCK. On success, ndl->nd_kck/kek/tk and
|
||||
* the corresponding *_len fields are populated and ptk_set=1.
|
||||
*
|
||||
* Spec §7.1.3.5: PTK PRF takes Data Interface addresses. Local NDI is
|
||||
* obtained via esp_wifi_get_mac(WIFI_IF_NAN); peer NDI lives in
|
||||
* ndl->peer_ndi, populated by ndp_response_indication on M2 RX.
|
||||
* Spec §7.1.3.5: PTK PRF takes Data Interface addresses resolved via
|
||||
* nan_ndp_ptk_addrs (peer NDI when assigned, else peer NMI).
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
static int ndl_ensure_ptk(struct ndl_info *ndl)
|
||||
{
|
||||
uint8_t i_addr[6];
|
||||
uint8_t r_addr[6];
|
||||
|
||||
if (ndl->ptk_set) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t our_mac[6];
|
||||
if (esp_wifi_get_mac(WIFI_IF_NAN, our_mac) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ndl_ensure_ptk: get our MAC failed");
|
||||
if (nan_ndp_ptk_addrs(ndl, false, i_addr, r_addr) != 0) {
|
||||
ESP_LOGE(TAG, "ndl_ensure_ptk: PTK address resolution failed");
|
||||
return -1;
|
||||
}
|
||||
if (nan_ndp_ptk_derive(ndl->security_ctx.nd_pmk,
|
||||
our_mac, /* IAddr = Initiator NDI (us) */
|
||||
ndl->peer_ndi, /* RAddr = Responder NDI (peer) */
|
||||
i_addr,
|
||||
r_addr,
|
||||
ndl->anonce, ndl->snonce,
|
||||
ndl->nd_kck, ndl->nd_kek, ndl->nd_tk) != 0) {
|
||||
ESP_LOGE(TAG, "ndl_ensure_ptk: PTK derivation failed");
|
||||
@@ -445,29 +486,32 @@ static bool nan_ndp_resp_resolve_pmk(struct ndl_info *ndl, const uint8_t *peer_n
|
||||
return have_peer_pmkid;
|
||||
}
|
||||
|
||||
bool pmk_resolved = false;
|
||||
|
||||
if (have_peer_pmkid) {
|
||||
struct own_svc_info *p_svc = nan_find_own_svc(ndl->publisher_id);
|
||||
int matched_idx = p_svc ? nan_match_pmkid(p_svc, ndl->security_ctx.nd_pmkid,
|
||||
ndl->peer_nmi, ndl->peer_ndi) : -1;
|
||||
if (matched_idx >= 0) {
|
||||
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||
ndl->security_ctx.csid_bitmap = p_svc->derived_security[matched_idx].csid_bitmap;
|
||||
memcpy(ndl->security_ctx.nd_pmk,
|
||||
p_svc->derived_security[matched_idx].nd_pmk,
|
||||
ESP_WIFI_NAN_NDP_PMK_LEN);
|
||||
ESP_LOGD(TAG, "NDP Resp Key Desc: resolved PMK from cred slot %d", matched_idx);
|
||||
pmk_resolved = true;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
|
||||
if (nan_security_fill_from_paired_cache(ndl, peer_nmi)) {
|
||||
return have_peer_pmkid;
|
||||
if (!pmk_resolved && need_pmk &&
|
||||
nan_security_fill_from_paired_cache(ndl, peer_nmi)) {
|
||||
ESP_LOGD(TAG, "NDP Resp Key Desc: resolved PMK from paired-peer cache");
|
||||
pmk_resolved = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!have_peer_pmkid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct own_svc_info *p_svc = nan_find_own_svc(ndl->publisher_id);
|
||||
int matched_idx = p_svc ? nan_match_pmkid(p_svc, ndl->security_ctx.nd_pmkid,
|
||||
ndl->peer_nmi, ndl->peer_ndi) : -1;
|
||||
if (matched_idx < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||
ndl->security_ctx.csid_bitmap = p_svc->derived_security[matched_idx].csid_bitmap;
|
||||
memcpy(ndl->security_ctx.nd_pmk,
|
||||
p_svc->derived_security[matched_idx].nd_pmk,
|
||||
ESP_WIFI_NAN_NDP_PMK_LEN);
|
||||
return true;
|
||||
return pmk_resolved;
|
||||
}
|
||||
|
||||
uint8_t esp_nan_get_ndp_resp_num_pmkids(uint8_t ndp_id, const uint8_t *peer_nmi)
|
||||
@@ -1253,15 +1297,16 @@ int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t n
|
||||
/* M2 echoes M1's replay counter unchanged (per RSNA 4-way handshake). */
|
||||
memcpy(ndl->tx_replay_counter, ndl->rx_replay_counter, NAN_REPLAY_COUNTER_LEN);
|
||||
|
||||
uint8_t our_mac[6];
|
||||
if (esp_wifi_get_mac(WIFI_IF_NAN, our_mac) != ESP_OK) {
|
||||
uint8_t i_addr[6];
|
||||
uint8_t r_addr[6];
|
||||
if (nan_ndp_ptk_addrs(ndl, true, i_addr, r_addr) != 0) {
|
||||
NAN_DATA_UNLOCK();
|
||||
ESP_LOGE(TAG, "NDP Resp Key Desc: get our MAC failed");
|
||||
ESP_LOGE(TAG, "NDP Resp Key Desc: PTK address resolution failed");
|
||||
return 0;
|
||||
}
|
||||
if (nan_ndp_ptk_derive(ndl->security_ctx.nd_pmk,
|
||||
ndl->peer_ndi, /* IAddr = Initiator NDI (Wi-Fi Aware v4.0 §7.1.3.5) */
|
||||
our_mac, /* RAddr = Responder NDI */
|
||||
i_addr, /* IAddr = Initiator NDI (Wi-Fi Aware v4.0 §7.1.3.5) */
|
||||
r_addr, /* RAddr = Responder NDI */
|
||||
ndl->anonce, ndl->snonce,
|
||||
ndl->nd_kck, ndl->nd_kek, ndl->nd_tk) != 0) {
|
||||
NAN_DATA_UNLOCK();
|
||||
@@ -1376,6 +1421,7 @@ int esp_nan_update_ndp_security_install_mic(uint8_t *m4_body, size_t body_len, u
|
||||
|
||||
void esp_nan_parse_ndp_csia(void *frm, size_t buf_len, wifi_nan_security_params_t *param)
|
||||
{
|
||||
|
||||
if (!frm || !param || buf_len < 3) {
|
||||
return;
|
||||
}
|
||||
@@ -1416,6 +1462,7 @@ void esp_nan_parse_ndp_csia(void *frm, size_t buf_len, wifi_nan_security_params_
|
||||
|
||||
void esp_nan_parse_ndp_scia(void *frm, size_t buf_len, wifi_nan_security_params_t *param)
|
||||
{
|
||||
|
||||
if (!frm || !param || buf_len < 3) {
|
||||
return;
|
||||
}
|
||||
@@ -1710,6 +1757,7 @@ void nan_security_apply_pending(struct ndl_info *ndl,
|
||||
const uint8_t *peer_nmi,
|
||||
const uint8_t *peer_ndi)
|
||||
{
|
||||
|
||||
if (ndl && s_pending_scia.pub_id == pub_id &&
|
||||
(s_pending_scia.has_csid || s_pending_scia.has_pmkid)) {
|
||||
|
||||
@@ -1721,6 +1769,8 @@ void nan_security_apply_pending(struct ndl_info *ndl,
|
||||
if (s_pending_scia.has_pmkid) {
|
||||
memcpy(ndl->security_ctx.nd_pmkid, s_pending_scia.pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN);
|
||||
int matched_idx = nan_match_pmkid(p_own_svc, s_pending_scia.pmkid, peer_nmi, peer_ndi);
|
||||
bool pmk_resolved = false;
|
||||
|
||||
if (matched_idx >= 0) {
|
||||
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||
ndl->security_ctx.csid_bitmap = p_own_svc->derived_security[matched_idx].csid_bitmap;
|
||||
@@ -1728,13 +1778,15 @@ void nan_security_apply_pending(struct ndl_info *ndl,
|
||||
p_own_svc->derived_security[matched_idx].nd_pmk,
|
||||
ESP_WIFI_NAN_NDP_PMK_LEN);
|
||||
ESP_LOGD(TAG, "NDP Indication: PMKID validated via cred slot %d", matched_idx);
|
||||
pmk_resolved = true;
|
||||
}
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
|
||||
else if (nan_security_fill_from_paired_cache(ndl, peer_nmi)) {
|
||||
ESP_LOGD(TAG, "NDP Indication: PMK sourced from paired-peer cache");
|
||||
pmk_resolved = true;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
if (!pmk_resolved) {
|
||||
ESP_LOGW(TAG, "NDP Indication: PMKID validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user