mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +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
|
||||
*/
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,10 @@ enum nan_role {
|
||||
* Pairing Initiator NMI || Pairing Responder NMI)).
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 bytes).
|
||||
* @param role enum nan_role value for the local device.
|
||||
* @param role @c NAN_ROLE_PAIRING_INITIATOR or @c NAN_ROLE_PAIRING_RESPONDER.
|
||||
* @param pairing_verification 1 when PASN ran as re-pair verification (NIRA verified on
|
||||
* Auth1 for responder, or @c cfg->pairing_verification on
|
||||
* initiator); 0 for bootstrap auth pairing.
|
||||
* @param ndp_csid NCS-SK CSID for paired-peer NDP (WIFI_NAN_CSID_NCS_SK_128
|
||||
* or _SK_256), 0 if no usable cipher mapping was available.
|
||||
* @param nd_pmk ND-PMK bytes (32) or NULL if KDK was absent.
|
||||
@@ -73,6 +76,7 @@ enum nan_role {
|
||||
*/
|
||||
typedef void (*esp_nan_pairing_key_installed_cb_t)(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,
|
||||
@@ -133,6 +137,23 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
|
||||
uint32_t nik_lifetime_sec,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
|
||||
|
||||
/**
|
||||
* @brief Schedule NAN PASN initiator verification (re-pair with cached NIK/PMK).
|
||||
*
|
||||
* Same role as @ref esp_nan_supp_pasn_initiator_auth but uses PASN verify instead of auth.
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 bytes).
|
||||
* @param pairing_key_installed_cb Callback invoked after pairwise key installation with peer NMI.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int esp_nan_supp_pasn_initiator_verify(const uint8_t *peer_nmi,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
|
||||
|
||||
/**
|
||||
* @brief Default pairing key-installed callback (NAN app layer).
|
||||
*/
|
||||
esp_nan_pairing_key_installed_cb_t esp_nan_pairing_get_key_installed_cb(void);
|
||||
|
||||
/**
|
||||
* Schedule @ref handle_auth_pasn from NAN app callback table.
|
||||
*/
|
||||
@@ -141,6 +162,26 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
|
||||
const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void);
|
||||
void nan_pasn_clear_saved_keys(void);
|
||||
|
||||
/**
|
||||
* Clear stale pairwise keys, saved PASN material, and active NDPs before
|
||||
* responder-side PASN verification (proactive pairing_start or passive Auth1).
|
||||
*/
|
||||
void nan_pasn_responder_verify_prepare(const uint8_t *peer_nmi);
|
||||
|
||||
/** Look up cached NPK for pairing verification (§7.6.5). Returns 0 on success.
|
||||
* Lookup matches peer_cred.service_hash against active own-service hashes,
|
||||
* or uses a single cached slot when only one NPK is present. */
|
||||
int nan_global_peer_npk_lookup(uint8_t *npk, size_t *npk_len, int *akmp);
|
||||
|
||||
/**
|
||||
* Mark/clear that the current PASN session for @a peer_nmi is pairing
|
||||
* verification (re-pair) and must not trigger NIK follow-up exchange. The
|
||||
* marker is anchored to own service @a own_inst_id (as resolved from the
|
||||
* verifying NIK), so it survives even when no peer_svc_info exists yet.
|
||||
*/
|
||||
void esp_nan_pairing_mark_verify_session(uint8_t own_inst_id, const uint8_t *peer_nmi);
|
||||
void esp_nan_pairing_clear_verify_session(const uint8_t *peer_nmi);
|
||||
|
||||
/**
|
||||
* Decrypt a NAN Shared Key Descriptor attribute received in a follow-up frame.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_private/esp_supp_nan.h"
|
||||
@@ -17,6 +18,8 @@ struct wpabuf;
|
||||
struct pasn_data;
|
||||
struct rsn_pmksa_cache;
|
||||
|
||||
#define NAN_PASN_PMKID_LEN 16
|
||||
|
||||
typedef esp_nan_pairing_key_installed_cb_t nan_pasn_pairing_key_installed_cb_t;
|
||||
|
||||
struct nan_config {
|
||||
@@ -43,20 +46,20 @@ struct nan_pasn_data {
|
||||
size_t pasn_ptk_len;
|
||||
struct pasn_data *pasn;
|
||||
nan_pasn_pairing_key_installed_cb_t pairing_key_installed_cb;
|
||||
uint8_t pairing_verification;
|
||||
uint32_t nik_lifetime_sec;
|
||||
bool pasn_auth2_done; /**< Initiator: Auth2 already processed (dedup) */
|
||||
uint8_t peer_npkid[NAN_PASN_PMKID_LEN]; /**< Nonce||Tag from peer NIRA; for PMKID cross-check */
|
||||
bool peer_npkid_valid;
|
||||
};
|
||||
|
||||
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr,
|
||||
int freq, int role, const uint8_t *bssid,
|
||||
int role, const uint8_t *bssid,
|
||||
const uint8_t *ssid, size_t ssid_len);
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr, int freq);
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr);
|
||||
struct nan_pasn_data *nan_pasn_data_init(void);
|
||||
void nan_pasn_data_deinit(struct nan_pasn_data *pd);
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq);
|
||||
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr, int freq);
|
||||
int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
const uint8_t *peer_addr, int freq, int role,
|
||||
const uint8_t *bssid,
|
||||
const uint8_t *ssid, size_t ssid_len);
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr);
|
||||
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr);
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "utils/includes.h"
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "common/nan.h"
|
||||
#include "esp_wifi_driver.h"
|
||||
#include "crypto/crypto.h"
|
||||
@@ -49,12 +50,74 @@
|
||||
|
||||
static struct nan_pasn_key_material g_nan_pasn_saved_keys;
|
||||
|
||||
/* Key index for esp_wifi_set_nan_key_internal (NAN PASN pairwise TK). */
|
||||
int temp = 1;
|
||||
#define NAN_NIRA_NONCE_LEN 8
|
||||
#define NAN_NIRA_TAG_LEN 8
|
||||
#define NAN_NIRA_ATTR_LEN 20
|
||||
#define NAN_PASN_CSIA_ATTR_MAX_LEN (3 + 1 + 2 * 8)
|
||||
#define NAN_PASN_PAIRING_BOOTSTRAP_METHODS WIFI_NAN_BOOTSTRAP_PIN_CODE_DISPLAY
|
||||
#define NAN_PASN_PAIRING_CSIA_PUB_ID 5
|
||||
|
||||
static int nan_pasn_npkid_from_nira_attr(const u8 *nira, u16 nira_len, u8 *npkid)
|
||||
{
|
||||
if (!nira || !npkid ||
|
||||
nira_len < 4 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* NPKID = Nonce || Tag (wire order matches NIRA body after Cipher Version;
|
||||
* PMKID is carried little-endian in the RSNE). */
|
||||
os_memcpy(npkid, nira + 4, NAN_NIRA_NONCE_LEN);
|
||||
os_memcpy(npkid + NAN_NIRA_NONCE_LEN,
|
||||
nira + 4 + NAN_NIRA_NONCE_LEN, NAN_NIRA_TAG_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nan_pasn_build_local_npkid(u8 *npkid)
|
||||
{
|
||||
uint8_t nira_frm[NAN_NIRA_ATTR_LEN];
|
||||
|
||||
if (!npkid ||
|
||||
esp_nan_construct_nira(nira_frm) <
|
||||
(int)(4 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nan_pasn_npkid_from_nira_attr(nira_frm, NAN_NIRA_ATTR_LEN, npkid);
|
||||
}
|
||||
|
||||
static int nan_validate_custom_pmkid(void *ctx, const u8 *addr, const u8 *pmkid)
|
||||
{
|
||||
struct nan_pasn_data *nan = ctx;
|
||||
|
||||
if (!pmkid) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pairing verification: NIK is already checked via NIRA and NPK via
|
||||
* PMKSA lookup, but §7.6.5 also binds RSNE PMKID to the same-frame NIRA
|
||||
* (NPKID = Nonce||Tag). Cross-check when we cached NPKID from that NIRA.
|
||||
*/
|
||||
if (!nan || !nan->peer_npkid_valid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (os_memcmp(pmkid, nan->peer_npkid, PMKID_LEN) != 0) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"NAN PASN verify: PMKID/NPKID mismatch for " MACSTR,
|
||||
MAC2STR(addr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NAN_PASN_AES_WRAP_OVERHEAD 8
|
||||
#define NAN_PASN_AES_WRAP_MIN_CIPHERTEXT (NAN_PASN_AES_WRAP_OVERHEAD + 8)
|
||||
|
||||
static int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
|
||||
|
||||
/**
|
||||
* Map PASN pairwise cipher to the NCS-SK CSID used for paired-peer NDPs
|
||||
* (Wi-Fi Aware v4.0 §7.6.4.2: paired NDP runs NCS-SK style M1-M4, key length
|
||||
@@ -239,7 +302,6 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
|
||||
struct wpa_ptk *ptk;
|
||||
uint8_t key_rsc[8] = {0};
|
||||
int kret;
|
||||
(void)nan;
|
||||
|
||||
if (!pasn) {
|
||||
return -1;
|
||||
@@ -258,7 +320,7 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "NAN PASN: NM-TK", ptk->tk, ptk->tk_len);
|
||||
kret = esp_wifi_set_nan_key_internal(
|
||||
NAN_PASN_WIFI_ALG_CCMP, pasn->peer_addr, temp, 1, key_rsc, sizeof(key_rsc),
|
||||
NAN_PASN_WIFI_ALG_CCMP, pasn->peer_addr, 1, 1, key_rsc, sizeof(key_rsc),
|
||||
ptk->tk, ptk->tk_len,
|
||||
NAN_KEY_NM_TK);
|
||||
if (kret != 0) {
|
||||
@@ -428,6 +490,21 @@ void nan_pasn_clear_saved_keys(void)
|
||||
forced_memzero(&g_nan_pasn_saved_keys, sizeof(g_nan_pasn_saved_keys));
|
||||
}
|
||||
|
||||
static void nan_pasn_clear_peer_tks_for_verify_start(const u8 *peer_nmi)
|
||||
{
|
||||
if (!peer_nmi) {
|
||||
return;
|
||||
}
|
||||
esp_nan_app_clear_peer_tks(peer_nmi, 0);
|
||||
nan_pasn_clear_saved_keys();
|
||||
}
|
||||
|
||||
void nan_pasn_responder_verify_prepare(const u8 *peer_nmi)
|
||||
{
|
||||
esp_nan_app_end_peer_datapaths(peer_nmi);
|
||||
nan_pasn_clear_peer_tks_for_verify_start(peer_nmi);
|
||||
}
|
||||
|
||||
/* NAN KDE OUI Type values from Wi-Fi Aware spec v4.0, Table 126. */
|
||||
#define NAN_PASN_KDE_OUI_TYPE_NIK 36
|
||||
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
|
||||
@@ -749,28 +826,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nan_chan_to_freq_mhz(uint8_t chan)
|
||||
{
|
||||
if (chan >= 1 && chan <= 13) {
|
||||
return 2407 + (int)chan * 5;
|
||||
}
|
||||
if (chan == 14) {
|
||||
return 2484;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nan_pasn_get_current_freq_mhz(void)
|
||||
{
|
||||
uint8_t primary = 0;
|
||||
wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE;
|
||||
|
||||
if (esp_wifi_get_channel(&primary, &second) != ESP_OK || primary == 0) {
|
||||
return 0;
|
||||
}
|
||||
return nan_chan_to_freq_mhz(primary);
|
||||
}
|
||||
|
||||
static void nan_pasn_auth_timeout_cancel(struct nan_pasn_data *nan);
|
||||
|
||||
static void nan_pasn_auth_timeout_cb(void *eloop_ctx, void *user_data)
|
||||
@@ -846,48 +901,97 @@ static int nan_set_dev_sae_pin(struct nan_pasn_data *nan, const char *digits)
|
||||
}
|
||||
|
||||
/*
|
||||
* Build Wi-Fi Alliance NAN vendor IE (EID 221) with DCEA / BPBA / CSIA for PASN
|
||||
* and pass to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends
|
||||
* Build Wi-Fi Alliance NAN vendor IE (EID 221) for PASN.
|
||||
* Bootstrap auth: DCEA + NPBA + CSIA.
|
||||
* Pairing verification: CSIA + NIRA only (no DCEA/NPBA).
|
||||
* Passed to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends
|
||||
* it after PASN Parameters on Auth 1/3 (and after prepare_data_element on Auth 2).
|
||||
*/
|
||||
static int nan_prepare_pasn_extra_ie(struct nan_pasn_data *nan, struct pasn_data *pasn,
|
||||
const struct wpabuf *frame, bool add_dira)
|
||||
const struct wpabuf *frame, bool include_nira)
|
||||
{
|
||||
static const u8 nan_pasn_attr_payload[] = {
|
||||
NAN_ATTR_DCEA, 0x02, 0x00, 0x00, 0x03,
|
||||
NAN_ATTR_BPBA, 0x05, 0x00, 0x90, 0x02, 0x00, 0x02, 0x00,
|
||||
NAN_ATTR_CSIA, 0x03, 0x00, 0x00, 0x07, 0x05,
|
||||
};
|
||||
u8 fixed[2 + 3 + 1 + sizeof(nan_pasn_attr_payload)];
|
||||
u8 csia_attr[NAN_PASN_CSIA_ATTR_MAX_LEN];
|
||||
u8 nira_attr[NAN_NIRA_ATTR_LEN];
|
||||
uint8_t *pairing_attrs = NULL;
|
||||
u8 *buf = NULL;
|
||||
u8 *pos;
|
||||
size_t fr_len = 0;
|
||||
size_t csia_len;
|
||||
size_t nira_len = 0;
|
||||
size_t attr_payload_len;
|
||||
size_t vendor_ie_len;
|
||||
size_t total_len;
|
||||
uint32_t npba_len = 0;
|
||||
uint32_t dcea_len = 0;
|
||||
uint32_t pairing_attrs_len = 0;
|
||||
int ret;
|
||||
|
||||
(void)nan;
|
||||
(void)add_dira;
|
||||
|
||||
fixed[0] = WLAN_EID_VENDOR_SPECIFIC;
|
||||
fixed[1] = 3 + 1 + sizeof(nan_pasn_attr_payload);
|
||||
WPA_PUT_BE24(&fixed[2], OUI_WFA);
|
||||
fixed[5] = NAN_OUI_TYPE;
|
||||
os_memcpy(&fixed[6], nan_pasn_attr_payload, sizeof(nan_pasn_attr_payload));
|
||||
ret = esp_nan_construct_csia(csia_attr, NAN_PASN_PAIRING_CSIA_PUB_ID,
|
||||
WIFI_NAN_CSID_BIT_NCS_PK_PASN_128, 0);
|
||||
if (ret <= 0 || ret > (int)sizeof(csia_attr)) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: CSIA build failed");
|
||||
return -1;
|
||||
}
|
||||
csia_len = (size_t) ret;
|
||||
attr_payload_len = csia_len;
|
||||
|
||||
if (include_nira) {
|
||||
ret = esp_nan_construct_nira(nira_attr);
|
||||
if (ret < (int)NAN_NIRA_ATTR_LEN) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: NIRA build failed");
|
||||
return -1;
|
||||
}
|
||||
nira_len = (size_t) ret;
|
||||
attr_payload_len += nira_len;
|
||||
} else {
|
||||
pairing_attrs = esp_wifi_nan_get_pairing_attrs(NAN_PASN_PAIRING_BOOTSTRAP_METHODS,
|
||||
true, true, &npba_len,
|
||||
&dcea_len, &pairing_attrs_len);
|
||||
if (!pairing_attrs || !pairing_attrs_len) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: pairing attrs build failed");
|
||||
return -1;
|
||||
}
|
||||
attr_payload_len += pairing_attrs_len;
|
||||
}
|
||||
|
||||
if (frame) {
|
||||
fr_len = wpabuf_len(frame);
|
||||
}
|
||||
|
||||
if (!fr_len) {
|
||||
return pasn_set_extra_ies(pasn, fixed, sizeof(fixed));
|
||||
vendor_ie_len = 3 + 1 + attr_payload_len;
|
||||
if (vendor_ie_len > UINT8_MAX) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: extra IE too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
total_len = sizeof(fixed) + fr_len;
|
||||
total_len = 2 + vendor_ie_len + fr_len;
|
||||
buf = os_malloc(total_len);
|
||||
if (!buf) {
|
||||
return -1;
|
||||
}
|
||||
os_memcpy(buf, fixed, sizeof(fixed));
|
||||
os_memcpy(buf + sizeof(fixed), wpabuf_head_u8(frame), fr_len);
|
||||
|
||||
pos = buf;
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = (u8) vendor_ie_len;
|
||||
WPA_PUT_BE24(pos, OUI_WFA);
|
||||
pos += 3;
|
||||
*pos++ = NAN_OUI_TYPE;
|
||||
if (pairing_attrs_len) {
|
||||
os_memcpy(pos, pairing_attrs, pairing_attrs_len);
|
||||
pos += pairing_attrs_len;
|
||||
}
|
||||
os_memcpy(pos, csia_attr, csia_len);
|
||||
pos += csia_len;
|
||||
if (nira_len) {
|
||||
os_memcpy(pos, nira_attr, nira_len);
|
||||
pos += nira_len;
|
||||
}
|
||||
if (fr_len) {
|
||||
os_memcpy(pos, wpabuf_head_u8(frame), fr_len);
|
||||
}
|
||||
|
||||
ret = pasn_set_extra_ies(pasn, buf, total_len);
|
||||
os_free(buf);
|
||||
return ret;
|
||||
@@ -962,7 +1066,7 @@ static void nan_pairing_apply_sae_pin(struct pasn_data *pasn, u8 pasn_type,
|
||||
pasn->password = nan->dev_sae_pin;
|
||||
}
|
||||
|
||||
void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bool verify, bool derive_kek)
|
||||
void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr)
|
||||
{
|
||||
struct pasn_data *pasn;
|
||||
struct wpabuf *rsnxe;
|
||||
@@ -971,6 +1075,8 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
|
||||
return;
|
||||
}
|
||||
|
||||
nan->peer_npkid_valid = false;
|
||||
|
||||
if (nan->pasn) {
|
||||
wpa_pasn_reset(nan->pasn);
|
||||
} else {
|
||||
@@ -1021,15 +1127,13 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
|
||||
* ND-PMK is filled by pasn_nd_pmk_derive_from_kdk_store (hostap
|
||||
* nan_crypto_derive_nd_pmk_from_kdk). Matches hostap nan_pairing.c.
|
||||
*/
|
||||
(void)derive_kek;
|
||||
pasn->derive_kek = false;
|
||||
pasn->kek_len = 0;
|
||||
|
||||
/* Wi-Fi Aware pairing: PASN Auth frames always use SAE as the base AKM. */
|
||||
pasn->akmp = WPA_KEY_MGMT_SAE;
|
||||
if (nan->dev_sae_pin_len > 0) {
|
||||
pasn->akmp = WPA_KEY_MGMT_SAE;
|
||||
nan_pairing_apply_sae_pin(pasn, nan->cfg->pasn_type, nan);
|
||||
} else if (!verify) {
|
||||
pasn->akmp = WPA_KEY_MGMT_PASN;
|
||||
}
|
||||
|
||||
pasn->rsn_pairwise = pasn->cipher;
|
||||
@@ -1059,16 +1163,19 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
|
||||
pasn->parse_data_element = nan->cfg->parse_data_element;
|
||||
pasn->validate_custom_pmkid = nan->cfg->pasn_validate_pmkid;
|
||||
|
||||
pasn->freq = freq;
|
||||
|
||||
}
|
||||
|
||||
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr,
|
||||
int freq, int role,
|
||||
int role,
|
||||
const u8 *bssid, const u8 *ssid, size_t ssid_len)
|
||||
{
|
||||
struct nan_pasn_data *nan;
|
||||
struct pasn_data *pasn;
|
||||
uint8_t npk[NAN_PASN_KEY_PMK_MAX];
|
||||
uint8_t own_addr[ETH_ALEN];
|
||||
u8 npkid[PMKID_LEN];
|
||||
size_t npk_len = 0;
|
||||
int akmp = WPA_KEY_MGMT_SAE;
|
||||
int ret = 0;
|
||||
|
||||
(void)role;
|
||||
@@ -1087,29 +1194,55 @@ int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr,
|
||||
}
|
||||
|
||||
nan->dev_role = NAN_ROLE_PAIRING_INITIATOR;
|
||||
nan_pasn_initialize(nan, peer_addr, freq, true, true);
|
||||
nan->pasn_auth2_done = false;
|
||||
nan->pairing_verification = 1;
|
||||
nan_pasn_clear_peer_tks_for_verify_start(peer_addr);
|
||||
nan_pasn_initialize(nan, peer_addr);
|
||||
pasn = nan->pasn;
|
||||
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: extra IE failed");
|
||||
if (!pasn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nan->cfg->pasn_validate_pmkid = nan_validate_custom_pmkid;
|
||||
pasn->validate_custom_pmkid = nan_validate_custom_pmkid;
|
||||
|
||||
/* §7.6.5: seed PMKSA with cached NPK and local NPKID (Nonce||Tag). */
|
||||
if (nan_global_peer_npk_lookup(npk, &npk_len, &akmp) != 0 ||
|
||||
esp_wifi_get_mac(WIFI_IF_NAN, own_addr) != ESP_OK ||
|
||||
nan_pasn_build_local_npkid(npkid) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pasn_initiator_pmksa_cache_add(nan->initiator_pmksa, own_addr,
|
||||
(u8 *) peer_addr, npk, npk_len, npkid) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pasn_set_akmp(pasn, akmp);
|
||||
pasn->wpa_key_mgmt = akmp;
|
||||
pasn_set_custom_pmkid(pasn, npkid);
|
||||
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, true) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
|
||||
pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
|
||||
pasn->akmp, pasn->cipher, pasn->group, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
wpa_printf(MSG_INFO, "PASN verify failed");
|
||||
ret = -1;
|
||||
}
|
||||
if (pasn->extra_ies) {
|
||||
os_free((u8 *) pasn->extra_ies);
|
||||
pasn->extra_ies = NULL;
|
||||
pasn->extra_ies_len = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
forced_memzero(npk, sizeof(npk));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr)
|
||||
{
|
||||
struct nan_pasn_data *nan;
|
||||
struct pasn_data *pasn;
|
||||
@@ -1126,10 +1259,16 @@ int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
|
||||
}
|
||||
|
||||
nan->dev_role = NAN_ROLE_PAIRING_INITIATOR;
|
||||
nan->pasn_auth2_done = false;
|
||||
nan->pairing_verification = 0;
|
||||
|
||||
nan_pasn_initialize(nan, addr, freq, false, true);
|
||||
nan_pasn_initialize(nan, addr);
|
||||
pasn = nan->pasn;
|
||||
|
||||
if (!pasn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr);
|
||||
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) {
|
||||
@@ -1138,7 +1277,7 @@ int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
|
||||
}
|
||||
|
||||
if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
|
||||
pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
|
||||
pasn->akmp, pasn->cipher, pasn->group, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
wpa_printf(MSG_INFO, "Failed to start PASN");
|
||||
ret = -1;
|
||||
@@ -1167,15 +1306,13 @@ int * int_array_dup(const int *a)
|
||||
}
|
||||
|
||||
static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
const struct ieee80211_auth *mgmt, size_t len,
|
||||
int freq)
|
||||
const struct ieee80211_auth *mgmt, size_t len)
|
||||
{
|
||||
struct pasn_data *pasn;
|
||||
u8 pasn_type;
|
||||
int pasn_groups[4] = { 0 };
|
||||
u16 auth_alg, auth_transaction, status_code;
|
||||
|
||||
(void)freq;
|
||||
if (!nan || !nan->pasn) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1246,6 +1383,7 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
nan_pasn_store_ptk(nan, &pasn->ptk);
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
nan_pasn_copy_keys_from_pasn(nan, pasn);
|
||||
/* Install NM-TK only after Auth3 is successfully validated. */
|
||||
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
|
||||
nan->pairing_key_installed_cb) {
|
||||
const uint8_t *nd_pmk = pasn_nd_pmk_global.valid ?
|
||||
@@ -1253,6 +1391,7 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
size_t nd_pmk_len = pasn_nd_pmk_global.valid ? PMK_LEN : 0;
|
||||
nan->pairing_key_installed_cb(pasn->peer_addr,
|
||||
(uint8_t)nan->dev_role,
|
||||
nan->pairing_verification,
|
||||
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
|
||||
nd_pmk, nd_pmk_len,
|
||||
nan->nik_lifetime_sec);
|
||||
@@ -1263,8 +1402,108 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a NIRA attribute inside a WFA NAN vendor-specific IE (OUI + type 0x13).
|
||||
* Returns a pointer to the attribute (ID + length + body) or NULL.
|
||||
*/
|
||||
static const u8 *nan_pasn_find_nira_attr(const u8 *ies, size_t ies_len, u16 *attr_len)
|
||||
{
|
||||
const u8 *ie = ies;
|
||||
const u8 *end = ies + ies_len;
|
||||
|
||||
while (ie + 2 <= end) {
|
||||
u8 id = ie[0];
|
||||
u8 elen = ie[1];
|
||||
|
||||
if (ie + 2 + elen > end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4 &&
|
||||
WPA_GET_BE24(ie + 2) == OUI_WFA && ie[5] == NAN_OUI_TYPE) {
|
||||
const u8 *pos = ie + 6;
|
||||
const u8 *attrs_end = ie + 2 + elen;
|
||||
|
||||
while (attrs_end - pos >= NAN_ATTR_HDR_LEN) {
|
||||
u8 attr_id = pos[0];
|
||||
u16 attr_body_len = WPA_GET_LE16(pos + 1);
|
||||
u16 total_attr_len;
|
||||
|
||||
if (attr_body_len > (size_t)(attrs_end - pos - NAN_ATTR_HDR_LEN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
total_attr_len = NAN_ATTR_HDR_LEN + attr_body_len;
|
||||
if (attr_id == NAN_ATTR_NIRA) {
|
||||
if (attr_len) {
|
||||
*attr_len = total_attr_len;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
pos += total_attr_len;
|
||||
}
|
||||
}
|
||||
ie += 2 + elen;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find NIRA in @a mgmt once, verify identity, and stash NPKID for PMKID
|
||||
* cross-check. When @a own_inst_id is non-NULL the matched own service id is
|
||||
* also returned (0 if none). */
|
||||
static bool nan_pasn_auth_process_nira(struct nan_pasn_data *nan,
|
||||
const u8 *peer_addr,
|
||||
const struct ieee80211_auth *mgmt,
|
||||
size_t len, u16 auth_trans,
|
||||
uint8_t *own_inst_id)
|
||||
{
|
||||
const u8 *var;
|
||||
size_t var_len;
|
||||
const u8 *nira;
|
||||
u16 nira_len = 0;
|
||||
|
||||
if (own_inst_id) {
|
||||
*own_inst_id = 0;
|
||||
}
|
||||
|
||||
if (!nan || !peer_addr || !mgmt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nan->peer_npkid_valid = false;
|
||||
|
||||
if (len < offsetof(struct ieee80211_auth, auth.variable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var = mgmt->auth.variable;
|
||||
var_len = len - offsetof(struct ieee80211_auth, auth.variable);
|
||||
nira = nan_pasn_find_nira_attr(var, var_len, &nira_len);
|
||||
if (!nira) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: NIRA missing in Auth%u from "
|
||||
MACSTR, auth_trans, MAC2STR(peer_addr));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!esp_nan_verify_nira_get_own_svc((u8 *) peer_addr, (u8 *) nira, nira_len,
|
||||
own_inst_id)) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: NIRA verification failed in "
|
||||
"Auth%u for " MACSTR, auth_trans, MAC2STR(peer_addr));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nan_pasn_npkid_from_nira_attr(nira, nira_len, nan->peer_npkid) == 0) {
|
||||
nan->peer_npkid_valid = true;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "NAN PASN verify: NIRA OK in Auth%u for " MACSTR,
|
||||
auth_trans, MAC2STR(peer_addr));
|
||||
return true;
|
||||
}
|
||||
|
||||
int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgmt,
|
||||
size_t len, int freq)
|
||||
size_t len)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 auth_transaction;
|
||||
@@ -1293,6 +1532,23 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
|
||||
|
||||
if (nan->dev_role == NAN_ROLE_PAIRING_INITIATOR &&
|
||||
auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) {
|
||||
/*
|
||||
* Duplicate Auth2: responder may retransmit if it didn't get Auth3
|
||||
* ACK. wpa_pasn_auth_rx() would reject with trans_seq mismatch and
|
||||
* we would also lose NM-TK that was already installed; silently
|
||||
* accept the retransmit instead.
|
||||
*/
|
||||
if (nan->pasn_auth2_done) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pasn->custom_pmkid_valid &&
|
||||
!nan_pasn_auth_process_nira(nan, mgmt->sa, mgmt, len, 2, NULL)) {
|
||||
wpa_printf(MSG_INFO, "PASN: Auth2 NIRA verify failed");
|
||||
nan->dev_role = NAN_ROLE_IDLE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
|
||||
if (ret < 0) {
|
||||
wpa_printf(MSG_INFO, "PASN: wpa_pasn_auth_rx() failed");
|
||||
@@ -1304,13 +1560,15 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
|
||||
* successfully built/transmitted by wpa_pasn_auth_rx().
|
||||
*/
|
||||
nan_pasn_copy_keys_from_pasn(nan, pasn);
|
||||
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
|
||||
nan->pairing_key_installed_cb) {
|
||||
nan->pasn_auth2_done = true;
|
||||
int tk_ret = nan_pasn_install_nan_pairwise_tk(nan, pasn);
|
||||
if (tk_ret == 0 && nan->pairing_key_installed_cb) {
|
||||
const uint8_t *nd_pmk = pasn_nd_pmk_global.valid ?
|
||||
pasn_nd_pmk_global.nd_pmk : NULL;
|
||||
size_t nd_pmk_len = pasn_nd_pmk_global.valid ? PMK_LEN : 0;
|
||||
nan->pairing_key_installed_cb(pasn->peer_addr,
|
||||
(uint8_t)nan->dev_role,
|
||||
nan->pairing_verification,
|
||||
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
|
||||
nd_pmk, nd_pmk_len,
|
||||
nan->nik_lifetime_sec);
|
||||
@@ -1321,7 +1579,7 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
|
||||
} else {
|
||||
ret = nan_handle_pasn_auth(nan, mgmt, len, freq);
|
||||
ret = nan_handle_pasn_auth(nan, mgmt, len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1330,7 +1588,8 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
|
||||
{
|
||||
const struct ieee80211_auth *mgmt;
|
||||
struct nan_pasn_data *nan;
|
||||
int rx_freq;
|
||||
bool auth1_verify = false;
|
||||
u8 auth1_pmkid[PMKID_LEN];
|
||||
|
||||
(void)trans_seq;
|
||||
(void)status;
|
||||
@@ -1340,15 +1599,114 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
|
||||
}
|
||||
mgmt = (const struct ieee80211_auth *)buf;
|
||||
nan = esp_nan_app_get_pasn_data();
|
||||
rx_freq = nan_pasn_get_current_freq_mhz();
|
||||
if (rx_freq <= 0) {
|
||||
rx_freq = 2412;
|
||||
if (!nan) {
|
||||
/*
|
||||
* Only lazy-init on Auth1: a stray Auth2/Auth3 (e.g. retransmit after
|
||||
* we already completed PASN and tore down the session) must be
|
||||
* dropped, otherwise we would init a fresh context and try to process
|
||||
* a mid-handshake frame against it, which fails noisily.
|
||||
*/
|
||||
if (le_to_host16(mgmt->auth.auth_transaction) !=
|
||||
WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
|
||||
return;
|
||||
}
|
||||
if (pasn_responder_init(mgmt->sa, UINT32_MAX,
|
||||
esp_nan_pairing_get_key_installed_cb()) != 0) {
|
||||
return;
|
||||
}
|
||||
nan = esp_nan_app_get_pasn_data();
|
||||
}
|
||||
if (!nan) {
|
||||
wpa_printf(MSG_DEBUG, "NAN PASN: receive_pasn: no context");
|
||||
return;
|
||||
}
|
||||
nan_pasn_auth_rx(nan, mgmt, len, rx_freq);
|
||||
|
||||
if (le_to_host16(mgmt->auth.auth_transaction) == 1) {
|
||||
struct ieee802_11_elems elems;
|
||||
struct wpa_ie_data rsn_data;
|
||||
struct wpa_pasn_params_data pasn_params;
|
||||
|
||||
if (ieee802_11_parse_elems(mgmt->auth.variable,
|
||||
len - offsetof(struct ieee80211_auth, auth.variable),
|
||||
&elems, 0) != ParseFailed &&
|
||||
elems.rsn_ie && elems.pasn_params &&
|
||||
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
|
||||
&rsn_data) == 0 &&
|
||||
rsn_data.num_pmkid &&
|
||||
wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
|
||||
elems.pasn_params_len + 3,
|
||||
false, &pasn_params) == 0 &&
|
||||
pasn_params.wrapped_data_format == WPA_PASN_WRAPPED_DATA_NO) {
|
||||
auth1_verify = true;
|
||||
os_memcpy(auth1_pmkid, rsn_data.pmkid, PMKID_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (auth1_verify) {
|
||||
uint8_t npk[NAN_PASN_KEY_PMK_MAX];
|
||||
uint8_t own_addr[ETH_ALEN];
|
||||
size_t npk_len = 0;
|
||||
int akmp = WPA_KEY_MGMT_SAE;
|
||||
struct pasn_data *pasn = nan->pasn;
|
||||
bool auth1_ok = false;
|
||||
uint8_t verify_own_inst_id = 0;
|
||||
|
||||
if (!nan_pasn_auth_process_nira(nan, mgmt->sa, mgmt, len, 1,
|
||||
&verify_own_inst_id)) {
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
nan_pasn_responder_verify_prepare(mgmt->sa);
|
||||
|
||||
if (!pasn) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: no PASN context for "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
if (nan_global_peer_npk_lookup(npk, &npk_len, &akmp) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: no cached NPK for "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
if (esp_wifi_get_mac(WIFI_IF_NAN, own_addr) != ESP_OK) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: failed to read NAN NMI");
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
if (pasn_responder_pmksa_cache_add(nan->responder_pmksa, own_addr,
|
||||
(u8 *) mgmt->sa, npk, npk_len,
|
||||
auth1_pmkid) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: PMKSA cache add failed for "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
|
||||
{
|
||||
u8 npkid[PMKID_LEN];
|
||||
|
||||
nan->cfg->pasn_validate_pmkid = nan_validate_custom_pmkid;
|
||||
pasn->validate_custom_pmkid = nan_validate_custom_pmkid;
|
||||
pasn_set_akmp(pasn, akmp);
|
||||
pasn->wpa_key_mgmt = akmp;
|
||||
if (nan_pasn_build_local_npkid(npkid) == 0) {
|
||||
pasn_set_custom_pmkid(pasn, npkid);
|
||||
}
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, true) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: extra IE (NIRA) build "
|
||||
"failed for " MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
auth1_ok = true;
|
||||
nan->pairing_verification = 1;
|
||||
}
|
||||
|
||||
auth1_verify_done:
|
||||
forced_memzero(npk, sizeof(npk));
|
||||
if (!auth1_ok) {
|
||||
return;
|
||||
}
|
||||
esp_nan_pairing_mark_verify_session(verify_own_inst_id, mgmt->sa);
|
||||
}
|
||||
|
||||
nan_pasn_auth_rx(nan, mgmt, len);
|
||||
}
|
||||
|
||||
void nan_pasn_pmksa_set_pmk(struct nan_pasn_data *nan, const u8 *src, const u8 *dst,
|
||||
@@ -1539,16 +1897,14 @@ void nan_pasn_data_deinit(struct nan_pasn_data *pd)
|
||||
os_free(pd);
|
||||
}
|
||||
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq)
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr)
|
||||
{
|
||||
if (!pd || !peer_addr) {
|
||||
return -1;
|
||||
}
|
||||
return nan_initiate_pasn_auth(pd, peer_addr, freq);
|
||||
return nan_initiate_pasn_auth(pd, peer_addr);
|
||||
}
|
||||
|
||||
#define NAN_PASN_VERIFY_ELOOP_SSID_MAX 32
|
||||
|
||||
struct nan_pasn_eloop_ctx {
|
||||
uint8_t peer_addr[ETH_ALEN];
|
||||
uint32_t pincode;
|
||||
@@ -1556,10 +1912,37 @@ struct nan_pasn_eloop_ctx {
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb;
|
||||
};
|
||||
|
||||
static struct nan_pasn_data *nan_pasn_eloop_prepare(struct nan_pasn_eloop_ctx *ctx,
|
||||
const char *pin_to_apply)
|
||||
{
|
||||
struct nan_pasn_data *old;
|
||||
struct nan_pasn_data *pd;
|
||||
|
||||
old = esp_nan_app_get_pasn_data();
|
||||
if (old) {
|
||||
nan_pasn_data_deinit(old);
|
||||
}
|
||||
|
||||
pd = nan_pasn_data_init();
|
||||
if (!pd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_nan_app_set_pasn_data(pd);
|
||||
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
|
||||
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
|
||||
|
||||
if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) {
|
||||
nan_pasn_data_deinit(pd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
{
|
||||
struct nan_pasn_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_data *old;
|
||||
struct nan_pasn_data *pd;
|
||||
char pin_digits[16];
|
||||
int n;
|
||||
@@ -1570,46 +1953,23 @@ static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
return;
|
||||
}
|
||||
|
||||
old = esp_nan_app_get_pasn_data();
|
||||
if (old) {
|
||||
nan_pasn_data_deinit(old);
|
||||
}
|
||||
|
||||
pd = nan_pasn_data_init();
|
||||
if (!pd) {
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_nan_app_set_pasn_data(pd);
|
||||
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
|
||||
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
|
||||
|
||||
if (ctx->pincode != UINT32_MAX) {
|
||||
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
|
||||
(unsigned)ctx->pincode);
|
||||
if (os_snprintf_error(sizeof(pin_digits), n)) {
|
||||
nan_pasn_data_deinit(pd);
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
pin_to_apply = pin_digits;
|
||||
}
|
||||
|
||||
if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) {
|
||||
nan_pasn_data_deinit(pd);
|
||||
pd = nan_pasn_eloop_prepare(ctx, pin_to_apply);
|
||||
if (!pd) {
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
int freq = nan_pasn_get_current_freq_mhz();
|
||||
|
||||
if (freq <= 0) {
|
||||
freq = 2412;
|
||||
}
|
||||
nan_pasn_auth_initiate(pd, ctx->peer_addr, freq);
|
||||
}
|
||||
nan_pasn_auth_initiate(pd, ctx->peer_addr);
|
||||
os_free(ctx);
|
||||
}
|
||||
|
||||
@@ -1619,8 +1979,12 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
|
||||
{
|
||||
struct nan_pasn_eloop_ctx *ctx;
|
||||
|
||||
if (!peer_nmi) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx = os_zalloc(sizeof(*ctx));
|
||||
if (!ctx || !peer_nmi) {
|
||||
if (!ctx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1637,49 +2001,32 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nan_pasn_verify_eloop_ctx {
|
||||
uint8_t peer_addr[ETH_ALEN];
|
||||
int freq;
|
||||
int role;
|
||||
uint8_t bssid[ETH_ALEN];
|
||||
uint8_t ssid[NAN_PASN_VERIFY_ELOOP_SSID_MAX];
|
||||
size_t ssid_len;
|
||||
};
|
||||
|
||||
static void nan_pasn_verify_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
static void nan_pasn_verify_init_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
{
|
||||
struct nan_pasn_verify_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_data *pd;
|
||||
const uint8_t *ssid_arg;
|
||||
|
||||
(void)eloop_ctx;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
pd = esp_nan_app_get_pasn_data();
|
||||
pd = nan_pasn_eloop_prepare(ctx, NULL);
|
||||
if (!pd) {
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ssid_arg = ctx->ssid_len ? ctx->ssid : NULL;
|
||||
nan_initiate_pasn_verify(pd, ctx->peer_addr, ctx->freq, ctx->role,
|
||||
ctx->bssid, ssid_arg, ctx->ssid_len);
|
||||
nan_initiate_pasn_verify(pd, ctx->peer_addr, 0, NULL, NULL, 0);
|
||||
os_free(ctx);
|
||||
}
|
||||
|
||||
int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
const uint8_t *peer_addr, int freq, int role,
|
||||
const uint8_t *bssid,
|
||||
const uint8_t *ssid, size_t ssid_len)
|
||||
int esp_nan_supp_pasn_initiator_verify(const uint8_t *peer_nmi,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
|
||||
{
|
||||
struct nan_pasn_verify_eloop_ctx *ctx;
|
||||
struct nan_pasn_eloop_ctx *ctx;
|
||||
|
||||
if (!peer_addr) {
|
||||
return -1;
|
||||
}
|
||||
if (ssid_len > NAN_PASN_VERIFY_ELOOP_SSID_MAX) {
|
||||
if (!peer_nmi) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1688,20 +2035,10 @@ int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memcpy(ctx->peer_addr, peer_addr, ETH_ALEN);
|
||||
ctx->freq = freq;
|
||||
ctx->role = role;
|
||||
if (bssid) {
|
||||
os_memcpy(ctx->bssid, bssid, ETH_ALEN);
|
||||
} else {
|
||||
os_memcpy(ctx->bssid, peer_addr, ETH_ALEN);
|
||||
}
|
||||
if (ssid && ssid_len) {
|
||||
os_memcpy(ctx->ssid, ssid, ssid_len);
|
||||
ctx->ssid_len = ssid_len;
|
||||
}
|
||||
os_memcpy(ctx->peer_addr, peer_nmi, ETH_ALEN);
|
||||
ctx->pairing_key_installed_cb = pairing_key_installed_cb;
|
||||
|
||||
if (eloop_register_timeout(secs, usecs, nan_pasn_verify_eloop_cb, NULL, ctx) != 0) {
|
||||
if (eloop_register_timeout(0, 0, nan_pasn_verify_init_eloop_cb, NULL, ctx) != 0) {
|
||||
os_free(ctx);
|
||||
return -1;
|
||||
}
|
||||
@@ -1717,9 +2054,11 @@ int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
*
|
||||
* @param peer_addr Peer NAN address, or NULL to use a broadcast placeholder until Auth1.
|
||||
* @param pincode 6-digit value 0..999999, or @c UINT32_MAX to keep default PIN from @ref nan_pasn_data_init.
|
||||
* @param pairing_key_installed_cb Callback stored on the responder PASN context.
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
|
||||
{
|
||||
struct nan_pasn_data *pd;
|
||||
struct nan_pasn_data *old;
|
||||
@@ -1728,11 +2067,20 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
const u8 *peer = peer_addr ? peer_addr : bcast;
|
||||
const char *pin_to_apply = NULL;
|
||||
int n;
|
||||
int freq;
|
||||
|
||||
os_memset(pin_digits, 0, sizeof(pin_digits));
|
||||
|
||||
old = esp_nan_app_get_pasn_data();
|
||||
/* Lazy Auth1 init and scheduled bootstrap init can both call this; do not
|
||||
* tear down an in-progress responder session for the same peer. */
|
||||
if (old && old->dev_role == NAN_ROLE_PAIRING_RESPONDER && old->pasn &&
|
||||
!is_broadcast_ether_addr(peer) &&
|
||||
os_memcmp(old->pasn->peer_addr, peer, ETH_ALEN) == 0) {
|
||||
if (pairing_key_installed_cb) {
|
||||
old->pairing_key_installed_cb = pairing_key_installed_cb;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (old) {
|
||||
nan_pasn_data_deinit(old);
|
||||
}
|
||||
@@ -1743,6 +2091,7 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
}
|
||||
|
||||
esp_nan_app_set_pasn_data(pd);
|
||||
pd->pairing_key_installed_cb = pairing_key_installed_cb;
|
||||
|
||||
if (pincode != UINT32_MAX) {
|
||||
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
|
||||
@@ -1757,13 +2106,8 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
freq = nan_pasn_get_current_freq_mhz();
|
||||
if (freq <= 0) {
|
||||
freq = 2412;
|
||||
}
|
||||
|
||||
pd->dev_role = NAN_ROLE_PAIRING_RESPONDER;
|
||||
nan_pasn_initialize(pd, peer, freq, false, true);
|
||||
nan_pasn_initialize(pd, peer);
|
||||
|
||||
if (!pd->pasn || nan_prepare_pasn_extra_ie(pd, pd->pasn, NULL, false) != 0) {
|
||||
goto fail;
|
||||
@@ -1786,16 +2130,15 @@ struct pasn_responder_eloop_ctx {
|
||||
static void pasn_responder_init_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
{
|
||||
struct pasn_responder_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_data *pd;
|
||||
|
||||
(void)eloop_ctx;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
if (pasn_responder_init(ctx->peer_addr, ctx->pincode) == 0) {
|
||||
pd = esp_nan_app_get_pasn_data();
|
||||
if (pasn_responder_init(ctx->peer_addr, ctx->pincode,
|
||||
ctx->pairing_key_installed_cb) == 0) {
|
||||
struct nan_pasn_data *pd = esp_nan_app_get_pasn_data();
|
||||
if (pd) {
|
||||
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
|
||||
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,6 +342,9 @@ uint8_t esp_wifi_op_class_supported_internal(uint8_t op_class, uint8_t min_chan,
|
||||
bool esp_wifi_is_wpa3_compatible_mode_enabled(uint8_t if_index);
|
||||
uint8_t esp_wifi_ap_get_owe_config_internal(void);
|
||||
esp_err_t esp_nan_complete_pairing(uint8_t svc_id, uint8_t peer_svc_id);
|
||||
uint8_t *esp_wifi_nan_get_pairing_attrs(uint16_t bootstrap_methods, bool pairing_enabled,
|
||||
bool nik_cache_enabled, uint32_t *npba_len,
|
||||
uint32_t *dcea_len, uint32_t *total_len);
|
||||
esp_err_t esp_wifi_nan_load_saved_creds(uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN], bool *own_nik_valid,
|
||||
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS], uint8_t *num_peer_creds);
|
||||
esp_err_t esp_wifi_nan_save_own_nik(const uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN]);
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
#define IEEE802_11_H
|
||||
|
||||
enum wpa_validate_result;
|
||||
int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr);
|
||||
#ifdef CONFIG_SAE
|
||||
int auth_sae_queue(struct hostapd_data *hapd, u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction);
|
||||
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 *buf, size_t len, u8 *bssid,
|
||||
u16 auth_transaction, u16 status);
|
||||
#endif /* CONFIG_SAE */
|
||||
u16 wpa_res_to_status_code(enum wpa_validate_result res);
|
||||
#ifdef CONFIG_OWE_SOFTAP
|
||||
uint16_t owe_process_assoc_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh,
|
||||
|
||||
@@ -52,7 +52,8 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||
unsigned int hash;
|
||||
|
||||
pmksa->pmksa_count--;
|
||||
pmksa->free_cb(entry, pmksa->ctx);
|
||||
if (pmksa->free_cb)
|
||||
pmksa->free_cb(entry, pmksa->ctx);
|
||||
|
||||
/* unlink from hash list */
|
||||
hash = PMKID_HASH(entry->pmkid);
|
||||
|
||||
@@ -48,7 +48,7 @@ enum nan_attr_id {
|
||||
NAN_ATTR_NDP_EXT = 0x29,
|
||||
NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */
|
||||
NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */
|
||||
NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
|
||||
NAN_ATTR_NPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
|
||||
NAN_ATTR_S3 = 0x2D,
|
||||
NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */
|
||||
NAN_ATTR_VENDOR_SPECIFIC = 0xDD,
|
||||
|
||||
@@ -624,6 +624,9 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
|
||||
#else /* CONFIG_IEEE80211R */
|
||||
goto fail;
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
} else if (verify && pasn->custom_pmkid_valid) {
|
||||
/* Wi-Fi Aware pairing verification: NPKID in RSNE, no wrapped data */
|
||||
pmkid = pasn->custom_pmkid;
|
||||
} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
|
||||
struct rsn_pmksa_cache_entry *pmksa;
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||
enum pmksa_free_reason reason)
|
||||
{
|
||||
pmksa->pmksa_count--;
|
||||
pmksa->free_cb(entry, pmksa->ctx, reason);
|
||||
if (pmksa->free_cb)
|
||||
pmksa->free_cb(entry, pmksa->ctx, reason);
|
||||
_pmksa_cache_free_entry(entry);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user