feat(esp_wifi): NAN Pairing Improvements and bugfixes

- Route NAN pairing bootstrap via NPBA receive path
- extend datapath_req wait time to fit secured M1-M4 handshake
- Plug ND-PMK derived from KDK into NDP
- prefers paired-peer cached ND-PMK (from PASN pairing complete), when available
- carry ND-PMK metadata in pairing install callback
- Extend PASN key-installed callback payload to include role, mapped NDP CSID
  and derived ND-PMK so the NAN layer can populate paired-peer security cache.

Co-authored-by: Akshat Agrawal <akshat.agrawal@espressif.com>
Co-authored-by: Sarvesh Bodakhe <sarvesh.bodakhe@espressif.com>
This commit is contained in:
Nachiket Kukade
2026-05-22 12:54:56 +08:00
committed by Sarvesh Bodakhe
parent 9f361f478d
commit 0f8d4b74a0
18 changed files with 659 additions and 403 deletions

View File

@@ -356,12 +356,10 @@ menu "Wi-Fi"
config ESP_WIFI_PASN_SUPPORT
bool "Enable PASN support"
depends on ESP_WIFI_NAN_PAIRING
default y if ESP_WIFI_NAN_PAIRING
default n
help
Enable PASN for Wi-Fi NAN; the supplicant exposes CONFIG_PASN when this is on.
This option is only available when "NAN-Sync pairing bootstrapping"
(ESP_WIFI_NAN_PAIRING) is enabled, because NAN PASN builds on that path.
Pre-Association Security Negotiation (PASN) for Wi-Fi NAN.
The wpa_supplicant exposes CONFIG_PASN when this is on.
config ESP_WIFI_SLP_IRAM_OPT
bool "WiFi SLP IRAM speed optimization"
@@ -628,12 +626,13 @@ menu "Wi-Fi"
config ESP_WIFI_NAN_PAIRING
bool "Enable NAN Pairing"
depends on ESP_WIFI_NAN_SYNC_ENABLE
depends on ESP_WIFI_NAN_SECURITY && IDF_EXPERIMENTAL_FEATURES && ESP_WIFI_MBEDTLS_CRYPTO
select ESP_WIFI_PASN_SUPPORT
default n
help
Enable NAN Pairing support. This allows devices to establish
secure pairing using bootstrapping methods like PIN code or
opportunistic pairing as defined in Wi-Fi Aware specification.
secure pairing using bootstrapping methods like PIN code.
Requires PASN in wpa_supplicant (ESP_WIFI_PASN_SUPPORT)
config ESP_WIFI_MBEDTLS_CRYPTO
bool "Use MbedTLS crypto APIs"

View File

@@ -73,22 +73,26 @@ typedef struct {
} wifi_nan_security_params_t;
/**
* @brief NAN peer security material parsed from Publish/Subscribe SDF SCIA
* @brief NAN peer security and pairing material parsed from Publish/Subscribe SDF
*
* @note Per Wi-Fi Aware v4.0 §7.1.3.5, a publisher SDF SCIA may advertise
* multiple ND-PMKIDs (one per cached PMK). The receiver stores them
* and matches against locally-derived PMKID at NDP-initiation time.
* Pairing flags mirror wifi_event_nan_svc_match_t and are filled by the
* NAN blob when parsing CSIA/SCIA/DCEA.
* Internal only — not part of the public API. Shared between WiFi
* libraries and NAN app layer.
*/
#define NAN_PEER_MAX_PMKIDS 2 /**< Internal cap; can grow without API impact */
typedef struct {
uint16_t csid_bitmap; /**< Peer's advertised Cipher Suite ID bitmap */
uint16_t csid_bitmap; /**< Peer's advertised Cipher Suite ID bitmap (WIFI_NAN_CSID_BIT_*) */
uint8_t num_pmkids; /**< Number of parsed PMKIDs */
uint8_t pmkids[NAN_PEER_MAX_PMKIDS][ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< Parsed ND-PMKIDs */
uint8_t group_data_prot: 1; /**< Peer advertises group data frame protection */
uint8_t group_mgmt_prot: 1; /**< Peer advertises group mgmt frame protection */
uint8_t reserved: 6; /**< Reserved */
uint8_t pairing_setup: 1; /**< Pairing setup: 0 - disabled, 1 - enabled */
uint8_t npk_nik_caching: 1; /**< NPK/NIK caching: 0 - disabled, 1 - enabled (valid if pairing_setup) */
uint8_t reserved: 4; /**< Reserved */
} wifi_nan_peer_sdf_security_t;
/* NAN Peer info parsed from SDF */
@@ -102,7 +106,6 @@ 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 */
uint8_t *shared_key_attr; /**< Shared key descriptor attribute, if any */
};
/* NDP Peer info parsed from NAF. */
@@ -114,6 +117,13 @@ struct ndp_cb_peer_info {
uint16_t ssi_len;
};
/* NAN Pairing Bootstrapping parameters parsed from SDF */
struct nan_cb_npba_t {
uint8_t type;
uint8_t status;
uint16_t methods;
};
/* Host-side callbacks the closed-source NAN blob fires up into nan_app.
* Registered once at nan_app init; all callbacks run in blob/WiFi-task
* context, so handlers must be non-blocking and avoid heavy work. */
@@ -121,8 +131,10 @@ struct nan_sync_callbacks {
/* Subscriber side: a Publish SDF matching the local subscribe was
* received from a peer.
* sub_id -- local subscribe service instance ID
* peer_info -- publisher details (see struct nan_cb_peer_info) */
void (* service_match)(uint8_t sub_id, struct nan_cb_peer_info *peer_info);
* peer_info -- publisher details (see struct nan_cb_peer_info)
* npba -- NPBA parsed from Publish SDF (Advertise), or NULL */
void (* service_match)(uint8_t sub_id, struct nan_cb_peer_info *peer_info,
struct nan_cb_npba_t *npba);
/* Publisher side: a Solicited Publish SDF was just transmitted in
* response to a Subscribe match. Fires once per recipient.
@@ -133,8 +145,14 @@ struct nan_sync_callbacks {
/* Either side: a Follow-up SDF was received for an existing service
* match (post-discovery messaging).
* svc_id -- local service instance ID this Follow-up targets
* peer_info -- sender MAC and SSI payload */
void (* receive)(uint8_t svc_id, struct nan_cb_peer_info *peer_info);
* peer_info -- sender MAC and SSI payload
* shared_key_attr -- Shared key descriptor attribute, if any
* shared_key_attr_buf_len -- total attribute length for pointer
* shared_key_attr (0 if shared_key_attr is NULL)
* npba -- NAN Pairing Bootstrapping parameters, if any */
void (* receive)(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
uint8_t *shared_key_attr, uint16_t shared_key_attr_len,
struct nan_cb_npba_t *npba);
/* Publisher side (NDP Responder): an NDP Request (M1) was received.
* Host either auto-accepts or waits for esp_wifi_nan_datapath_resp()
@@ -174,8 +192,6 @@ struct nan_sync_callbacks {
* host can populate ndl->peer_ndi for spec-correct PTK derivation
* (§7.1.3.5: PTK uses Data Interface addresses). */
void (* ndp_response_indication)(struct ndp_cb_peer_info *peer_info);
void (* pairing_indication)(uint8_t peer_svc_id, uint8_t pub_id, uint8_t peer_nmi[6], uint16_t selected_method);
void (* pairing_confirm)(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);
void (* receive_pasn)(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status);
uint32_t (* get_nira_len)(void);
int (* construct_nira)(uint8_t *frm);
@@ -1181,23 +1197,6 @@ uint32_t esp_nan_get_nira_len(void);
*/
int esp_nan_construct_nira(uint8_t *frm);
/**
* @brief Get length of Management MIC Element (MME)
*
* @return Length in bytes
*/
uint32_t esp_nan_get_mme_len(void);
/**
* @brief Construct dummy Management MIC Element (MME)
*
* @param[out] frm Buffer to write the element to
* @param[in] ipn Incremental Packet Number (6-byte IPN, lower 4 bytes used from this param)
*
* @return Number of bytes written
*/
int esp_nan_construct_mme(uint8_t *frm, uint32_t ipn);
/**
* @brief Get the time information from the MAC clock. The time is precise only if modem sleep or light sleep is not enabled.
*

View File

@@ -979,10 +979,10 @@ typedef struct {
} wifi_nan_discovery_security_params_t;
/**
* @brief NAN Pairing Bootstrapping Method bitmask (per Wi-Fi Aware 4.0 spec)
* @brief NAN Pairing Bootstrapping Method bitmap (per Wi-Fi Aware 4.0 spec)
*
* Each bit corresponds to a bootstrapping method capability.
* Multiple bits can be set simultaneously in the NPBA attribute.
* Each bit corresponds to one bootstrapping method (WIFI_NAN_BOOTSTRAP_*).
* Multiple bits may be set in bootstrapping_methods / NPBA.
*/
#define WIFI_NAN_BOOTSTRAP_OPPORTUNISTIC BIT(0) /**< Opportunistic bootstrapping */
#define WIFI_NAN_BOOTSTRAP_PIN_CODE_DISPLAY BIT(1) /**< Pin-code display */
@@ -1003,7 +1003,7 @@ typedef struct {
uint8_t npk_nik_caching: 1; /**< 0 - NPK/NIK caching disabled, 1 - enabled */
uint8_t pairing_verification: 1; /**< 0 - Pairing verification disabled, 1 - enabled */
uint8_t reserved: 5; /**< Reserved bits */
uint16_t bootstrapping_methods; /**< Bitmask of wifi_nan_bootstrap_method_t */
uint16_t bootstrapping_methods; /**< Bitmap of WIFI_NAN_BOOTSTRAP_* method bits */
uint16_t comeback_delay; /**< Comeback delay in TUs, 0 if comeback not required */
} wifi_nan_pairing_cfg_t;
@@ -1056,7 +1056,7 @@ typedef struct {
The driver makes a private copy during esp_wifi_nan_publish_service();
the caller may free this immediately after the call returns. */
nan_vendor_ie_t *vendor_ie; /**< Vendor specific IE to be added in publish frames */
wifi_nan_pairing_cfg_t pairing; /**< Pairing configuration parameters */
wifi_nan_pairing_cfg_t *pairing; /**< Pairing configuration parameters */
} wifi_nan_publish_cfg_t;
/**
@@ -1083,7 +1083,7 @@ typedef struct {
The driver makes a private copy during esp_wifi_nan_subscribe_service();
the caller may free this immediately after the call returns. */
nan_vendor_ie_t *vendor_ie; /**< Vendor specific IE to be added in subscribe frames */
wifi_nan_pairing_cfg_t pairing; /**< Pairing configuration parameters */
wifi_nan_pairing_cfg_t *pairing; /**< Pairing configuration parameters */
} wifi_nan_subscribe_cfg_t;
/**
@@ -1292,10 +1292,6 @@ typedef enum {
WIFI_EVENT_NDP_INDICATION, /**< Received NDP Request from a NAN Peer */
WIFI_EVENT_NDP_CONFIRM, /**< NDP Confirm Indication */
WIFI_EVENT_NDP_TERMINATED, /**< NAN Datapath terminated indication */
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 PASN pairwise key installation completed */
WIFI_EVENT_HOME_CHANNEL_CHANGE, /**< Wi-Fi home channel changedoesn't occur when scanning */
WIFI_EVENT_STA_NEIGHBOR_REP, /**< Received Neighbor Report response */
@@ -1306,6 +1302,10 @@ typedef enum {
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
WIFI_EVENT_DPP_CFG_RECVD, /**< Config received via DPP Authentication */
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 PASN pairwise key installation completed */
WIFI_EVENT_MAX, /**< Invalid Wi-Fi event ID */
} wifi_event_t;
@@ -1543,21 +1543,24 @@ typedef struct {
* @brief Argument structure for WIFI_EVENT_NAN_SVC_MATCH event
*/
typedef struct {
uint8_t subscribe_id; /**< Subscribe Service Identifier */
uint8_t publish_id; /**< Publish Service Identifier */
uint8_t pub_if_mac[6]; /**< NAN Interface MAC of the Publisher */
bool update_pub_id; /**< Indicates whether publisher's service ID needs to be updated */
uint8_t datapath_reqd: 1; /**< NAN Datapath required for the service */
uint8_t fsd_reqd: 1; /**< Further Service Discovery(FSD) required */
uint8_t fsd_gas: 1; /**< 0 - Follow-up used for FSD, 1 - GAS used for FSD */
uint8_t ndpe_support: 1; /**< NDPE supported by peer */
uint8_t security_reqd: 1; /**< Security: 0 - Open, 1 - Required (NDP Security) */
uint8_t reserved: 3; /**< Reserved */
uint32_t reserved_1; /**< Reserved */
uint32_t reserved_2; /**< Reserved */
uint8_t ssi_version; /**< Indicates version of SSI in Publish instance, 0 if not available */
uint16_t ssi_len; /**< Length of service specific info */
uint8_t ssi[]; /**< Service specific info of Publisher */
uint8_t subscribe_id; /**< Subscribe Service Identifier */
uint8_t publish_id; /**< Publish Service Identifier */
uint8_t pub_if_mac[6]; /**< NAN Interface MAC of the Publisher */
bool update_pub_id; /**< Indicates whether publisher's service ID needs to be updated */
uint8_t datapath_reqd: 1; /**< NAN Datapath required for the service */
uint8_t fsd_reqd: 1; /**< Further Service Discovery(FSD) required */
uint8_t fsd_gas: 1; /**< 0 - Follow-up used for FSD, 1 - GAS used for FSD */
uint8_t ndpe_support: 1; /**< NDPE supported by peer */
uint8_t security_reqd: 1; /**< Security: 0 - Open, 1 - Required (NDP Security) */
uint8_t pairing_setup: 1; /**< Pairing setup: 0 - disabled, 1 - enabled (needs security_reqd:1) */
uint8_t npk_nik_caching: 1; /**< NPK/NIK caching: 0 - disabled, 1 - enabled (valid if pairing_setup:1)*/
uint8_t already_paired: 1; /**< 0 - not paired, 1 - device already paired */
uint16_t csid_bitmap; /**< Supported Cipher Suite bitmap, each bit of type WIFI_NAN_CSID_BIT_[] */
uint16_t bootstrapping_methods; /**< Peer advertised bootstrapping methods (WIFI_NAN_BOOTSTRAP_* bitmap) */
uint32_t reserved_2; /**< Reserved */
uint8_t ssi_version; /**< Indicates version of SSI in Publish instance, 0 if not available */
uint16_t ssi_len; /**< Length of service specific info */
uint8_t ssi[]; /**< Service specific info of Publisher */
} wifi_event_nan_svc_match_t;
/**
@@ -1627,13 +1630,13 @@ typedef struct {
* @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_pairing_response().
* 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 (wifi_nan_bootstrap_method_t) */
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;
@@ -1649,7 +1652,7 @@ 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 matched_method; /**< Matched bootstrapping method (valid if accepted) */
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) */

View File

@@ -335,12 +335,10 @@ config WIFI_RMT_ENABLE_WPA3_OWE_SOFTAP
config WIFI_RMT_PASN_SUPPORT
bool "Enable PASN support"
depends on WIFI_RMT_NAN_PAIRING
default y if WIFI_RMT_NAN_PAIRING
default n
help
Enable PASN for Wi-Fi NAN; the supplicant exposes CONFIG_PASN when this is on.
This option is only available when "NAN-Sync pairing bootstrapping"
(WIFI_RMT_NAN_PAIRING) is enabled, because NAN PASN builds on that path.
Pre-Association Security Negotiation (PASN) for Wi-Fi NAN.
The wpa_supplicant exposes CONFIG_PASN when this is on.
config WIFI_RMT_SLP_IRAM_OPT
bool "WiFi SLP IRAM speed optimization"
@@ -607,12 +605,13 @@ config WIFI_RMT_NAN_USD_ENABLE
config WIFI_RMT_NAN_PAIRING
bool "Enable NAN Pairing"
depends on WIFI_RMT_NAN_SYNC_ENABLE
depends on WIFI_RMT_NAN_SECURITY && IDF_EXPERIMENTAL_FEATURES && WIFI_RMT_MBEDTLS_CRYPTO
select WIFI_RMT_PASN_SUPPORT
default n
help
Enable NAN Pairing support. This allows devices to establish
secure pairing using bootstrapping methods like PIN code or
opportunistic pairing as defined in Wi-Fi Aware specification.
secure pairing using bootstrapping methods like PIN code.
Requires PASN in wpa_supplicant (WIFI_RMT_PASN_SUPPORT)
config WIFI_RMT_MBEDTLS_CRYPTO
bool "Use MbedTLS crypto APIs"

View File

@@ -152,7 +152,6 @@ endif
if WIFI_RMT_PASN_SUPPORT
config ESP_WIFI_PASN_SUPPORT # ignore: multiple-definition
bool
depends on WIFI_RMT_NAN_PAIRING
default WIFI_RMT_PASN_SUPPORT
endif
@@ -314,7 +313,7 @@ endif
if WIFI_RMT_NAN_PAIRING
config ESP_WIFI_NAN_PAIRING # ignore: multiple-definition
bool
depends on WIFI_RMT_NAN_SYNC_ENABLE
depends on WIFI_RMT_NAN_SECURITY && IDF_EXPERIMENTAL_FEATURES && WIFI_RMT_MBEDTLS_CRYPTO
default WIFI_RMT_NAN_PAIRING
endif

View File

@@ -979,10 +979,10 @@ typedef struct {
} wifi_nan_discovery_security_params_t;
/**
* @brief NAN Pairing Bootstrapping Method bitmask (per Wi-Fi Aware 4.0 spec)
* @brief NAN Pairing Bootstrapping Method bitmap (per Wi-Fi Aware 4.0 spec)
*
* Each bit corresponds to a bootstrapping method capability.
* Multiple bits can be set simultaneously in the NPBA attribute.
* Each bit corresponds to one bootstrapping method (WIFI_NAN_BOOTSTRAP_*).
* Multiple bits may be set in bootstrapping_methods / NPBA.
*/
#define WIFI_NAN_BOOTSTRAP_OPPORTUNISTIC BIT(0) /**< Opportunistic bootstrapping */
#define WIFI_NAN_BOOTSTRAP_PIN_CODE_DISPLAY BIT(1) /**< Pin-code display */
@@ -1003,7 +1003,7 @@ typedef struct {
uint8_t npk_nik_caching: 1; /**< 0 - NPK/NIK caching disabled, 1 - enabled */
uint8_t pairing_verification: 1; /**< 0 - Pairing verification disabled, 1 - enabled */
uint8_t reserved: 5; /**< Reserved bits */
uint16_t bootstrapping_methods; /**< Bitmask of wifi_nan_bootstrap_method_t */
uint16_t bootstrapping_methods; /**< Bitmap of WIFI_NAN_BOOTSTRAP_* method bits */
uint16_t comeback_delay; /**< Comeback delay in TUs, 0 if comeback not required */
} wifi_nan_pairing_cfg_t;
@@ -1056,7 +1056,7 @@ typedef struct {
The driver makes a private copy during esp_wifi_nan_publish_service();
the caller may free this immediately after the call returns. */
nan_vendor_ie_t *vendor_ie; /**< Vendor specific IE to be added in publish frames */
wifi_nan_pairing_cfg_t pairing; /**< Pairing configuration parameters */
wifi_nan_pairing_cfg_t *pairing; /**< Pairing configuration parameters */
} wifi_nan_publish_cfg_t;
/**
@@ -1083,7 +1083,7 @@ typedef struct {
The driver makes a private copy during esp_wifi_nan_subscribe_service();
the caller may free this immediately after the call returns. */
nan_vendor_ie_t *vendor_ie; /**< Vendor specific IE to be added in subscribe frames */
wifi_nan_pairing_cfg_t pairing; /**< Pairing configuration parameters */
wifi_nan_pairing_cfg_t *pairing; /**< Pairing configuration parameters */
} wifi_nan_subscribe_cfg_t;
/**
@@ -1292,10 +1292,6 @@ typedef enum {
WIFI_EVENT_NDP_INDICATION, /**< Received NDP Request from a NAN Peer */
WIFI_EVENT_NDP_CONFIRM, /**< NDP Confirm Indication */
WIFI_EVENT_NDP_TERMINATED, /**< NAN Datapath terminated indication */
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 PASN pairwise key installation completed */
WIFI_EVENT_HOME_CHANNEL_CHANGE, /**< Wi-Fi home channel changedoesn't occur when scanning */
WIFI_EVENT_STA_NEIGHBOR_REP, /**< Received Neighbor Report response */
@@ -1306,6 +1302,10 @@ typedef enum {
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
WIFI_EVENT_DPP_CFG_RECVD, /**< Config received via DPP Authentication */
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 PASN pairwise key installation completed */
WIFI_EVENT_MAX, /**< Invalid Wi-Fi event ID */
} wifi_event_t;
@@ -1543,21 +1543,24 @@ typedef struct {
* @brief Argument structure for WIFI_EVENT_NAN_SVC_MATCH event
*/
typedef struct {
uint8_t subscribe_id; /**< Subscribe Service Identifier */
uint8_t publish_id; /**< Publish Service Identifier */
uint8_t pub_if_mac[6]; /**< NAN Interface MAC of the Publisher */
bool update_pub_id; /**< Indicates whether publisher's service ID needs to be updated */
uint8_t datapath_reqd: 1; /**< NAN Datapath required for the service */
uint8_t fsd_reqd: 1; /**< Further Service Discovery(FSD) required */
uint8_t fsd_gas: 1; /**< 0 - Follow-up used for FSD, 1 - GAS used for FSD */
uint8_t ndpe_support: 1; /**< NDPE supported by peer */
uint8_t security_reqd: 1; /**< Security: 0 - Open, 1 - Required (NDP Security) */
uint8_t reserved: 3; /**< Reserved */
uint32_t reserved_1; /**< Reserved */
uint32_t reserved_2; /**< Reserved */
uint8_t ssi_version; /**< Indicates version of SSI in Publish instance, 0 if not available */
uint16_t ssi_len; /**< Length of service specific info */
uint8_t ssi[]; /**< Service specific info of Publisher */
uint8_t subscribe_id; /**< Subscribe Service Identifier */
uint8_t publish_id; /**< Publish Service Identifier */
uint8_t pub_if_mac[6]; /**< NAN Interface MAC of the Publisher */
bool update_pub_id; /**< Indicates whether publisher's service ID needs to be updated */
uint8_t datapath_reqd: 1; /**< NAN Datapath required for the service */
uint8_t fsd_reqd: 1; /**< Further Service Discovery(FSD) required */
uint8_t fsd_gas: 1; /**< 0 - Follow-up used for FSD, 1 - GAS used for FSD */
uint8_t ndpe_support: 1; /**< NDPE supported by peer */
uint8_t security_reqd: 1; /**< Security: 0 - Open, 1 - Required (NDP Security) */
uint8_t pairing_setup: 1; /**< Pairing setup: 0 - disabled, 1 - enabled (needs security_reqd:1) */
uint8_t npk_nik_caching: 1; /**< NPK/NIK caching: 0 - disabled, 1 - enabled (valid if pairing_setup:1)*/
uint8_t already_paired: 1; /**< 0 - not paired, 1 - device already paired */
uint16_t csid_bitmap; /**< Supported Cipher Suite bitmap, each bit of type WIFI_NAN_CSID_BIT_[] */
uint16_t bootstrapping_methods; /**< Peer advertised bootstrapping methods (WIFI_NAN_BOOTSTRAP_* bitmap) */
uint32_t reserved_2; /**< Reserved */
uint8_t ssi_version; /**< Indicates version of SSI in Publish instance, 0 if not available */
uint16_t ssi_len; /**< Length of service specific info */
uint8_t ssi[]; /**< Service specific info of Publisher */
} wifi_event_nan_svc_match_t;
/**
@@ -1627,13 +1630,13 @@ typedef struct {
* @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_pairing_response().
* 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 (wifi_nan_bootstrap_method_t) */
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;
@@ -1649,7 +1652,7 @@ 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 matched_method; /**< Matched bootstrapping method (valid if accepted) */
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) */

View File

@@ -218,7 +218,7 @@ typedef struct {
uint8_t inst_id; /**< Own service instance id */
uint8_t peer_inst_id; /**< Peer's service instance id */
uint8_t peer_mac[6]; /**< Peer's NAN Management Interface MAC */
uint16_t selected_method; /**< One selected method from wifi_nan_bootstrap_method_t */
uint16_t selected_method; /**< One selected WIFI_NAN_BOOTSTRAP_* method bit */
wifi_nan_pairing_status_t status; /**< Set to COMEBACK when resending with cookie */
uint16_t comeback_after; /**< Comeback deferral time in TUs (only when status=COMEBACK) */
uint32_t cookie; /**< Opaque cookie from responder (only when status=COMEBACK) */
@@ -235,7 +235,7 @@ typedef struct {
uint8_t peer_inst_id; /**< Peer's service instance id */
uint8_t peer_mac[6]; /**< Peer's NAN Management Interface MAC */
wifi_nan_pairing_status_t status; /**< Accepted, Rejected, or Comeback */
uint16_t matched_method; /**< Matched bootstrapping method (valid if accepted) */
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
uint8_t reason_code; /**< Rejection reason code (valid if rejected) */
uint16_t comeback_after; /**< Comeback deferral time in TUs (only when status=COMEBACK) */
uint32_t cookie; /**< Opaque cookie for comeback (only when status=COMEBACK) */
@@ -261,7 +261,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_PAIRING_INDICATION event.
* WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event.
*
* @param resp Pairing bootstrapping response parameters.
*

View File

@@ -15,6 +15,7 @@
#include "esp_log.h"
#include "esp_check.h"
#include "esp_mac.h"
#include "esp_timer.h"
#include "os.h"
#include "esp_nan.h"
#include "utils/common.h"
@@ -22,6 +23,44 @@
#ifdef CONFIG_ESP_WIFI_NAN_USD_ENABLE
#include "esp_private/esp_nan_usd.h"
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
bool esp_nan_ndp_info_present(void)
{
return true;
}
uint32_t esp_nan_ndp_get_info_len(void)
{
return 12;
}
int esp_nan_construct_ndp_info(uint8_t *frm)
{
static const uint8_t ndp_info_attr[] = {
0x01, 0x09, 0x00, 0x50, 0x6f, 0x9a, 0x02, 0x00, 0x02, 0x00, 0x05, 0x0d
};
if (!frm) {
return 0;
}
memcpy(frm, ndp_info_attr, sizeof(ndp_info_attr));
return sizeof(ndp_info_attr);
}
#if !CONFIG_ESP_WIFI_NAN_PAIRING
uint32_t esp_nan_get_nira_len(void)
{
return 0;
}
int esp_nan_construct_nira(uint8_t *frm)
{
(void)frm;
return 0;
}
#endif
#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
#include "esp_private/esp_supp_nan.h"
#include "apps_private/wifi_apps_private.h"
@@ -85,6 +124,118 @@ const uint8_t *nan_pairing_get_null_mac(void)
{
return null_mac;
}
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/* ---- Paired-peer cache ---------------------------------------------------
*
* Tracks ND-PMK + cipher per peer NMI after PASN/Pairing completes. The
* security layer (nan_security.c) consumes these on inbound SDF match and on
* NDP request/response to source ND-PMK without an app-provided service
* credential. Lifetime accounting is timestamp-based; auto-purge is left to
* the consumer (current path: explicit remove or stop).
*/
static struct nan_paired_peer *nan_app_find_paired_slot_locked(const uint8_t *peer_nmi)
{
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
struct nan_paired_peer *p = &s_nan_ctx.paired_peers[i];
if (p->valid && memcmp(p->peer_nmi, peer_nmi, MACADDR_LEN) == 0) {
return p;
}
}
return NULL;
}
static struct nan_paired_peer *nan_app_alloc_paired_slot_locked(const uint8_t *peer_nmi)
{
struct nan_paired_peer *p = nan_app_find_paired_slot_locked(peer_nmi);
if (p) {
return p;
}
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
if (!s_nan_ctx.paired_peers[i].valid) {
return &s_nan_ctx.paired_peers[i];
}
}
/* Evict oldest entry by established_us. */
int oldest = 0;
int64_t oldest_ts = s_nan_ctx.paired_peers[0].established_us;
for (int i = 1; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
if (s_nan_ctx.paired_peers[i].established_us < oldest_ts) {
oldest_ts = s_nan_ctx.paired_peers[i].established_us;
oldest = i;
}
}
return &s_nan_ctx.paired_peers[oldest];
}
esp_err_t nan_app_register_paired_peer(const uint8_t *peer_nmi,
uint8_t role,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len,
uint32_t lifetime_sec)
{
if (!peer_nmi || !nd_pmk || nd_pmk_len != ESP_WIFI_NAN_NDP_PMK_LEN) {
return ESP_ERR_INVALID_ARG;
}
if (ndp_csid != WIFI_NAN_CSID_NCS_SK_128 && ndp_csid != WIFI_NAN_CSID_NCS_SK_256) {
return ESP_ERR_INVALID_ARG;
}
NAN_DATA_LOCK();
struct nan_paired_peer *p = nan_app_alloc_paired_slot_locked(peer_nmi);
forced_memzero(p->nd_pmk, sizeof(p->nd_pmk));
memset(p, 0, sizeof(*p));
p->valid = true;
MACADDR_COPY(p->peer_nmi, peer_nmi);
p->role = role;
p->ndp_csid = ndp_csid;
memcpy(p->nd_pmk, nd_pmk, nd_pmk_len);
p->lifetime_sec = lifetime_sec;
p->established_us = esp_timer_get_time();
NAN_DATA_UNLOCK();
ESP_LOGI(TAG, "Paired peer cached: " MACSTR " role=%u csid=%u lifetime=%us",
MAC2STR(peer_nmi), role, ndp_csid, lifetime_sec);
return ESP_OK;
}
const struct nan_paired_peer *nan_app_find_paired_peer(const uint8_t *peer_nmi)
{
if (!peer_nmi) {
return NULL;
}
/* Caller holds NAN_DATA_LOCK (see nan_i.h). */
return nan_app_find_paired_slot_locked(peer_nmi);
}
void nan_app_remove_paired_peer(const uint8_t *peer_nmi)
{
if (!peer_nmi) {
return;
}
NAN_DATA_LOCK();
struct nan_paired_peer *p = nan_app_find_paired_slot_locked(peer_nmi);
if (p) {
forced_memzero(p->nd_pmk, sizeof(p->nd_pmk));
memset(p, 0, sizeof(*p));
}
NAN_DATA_UNLOCK();
}
void nan_app_clear_paired_peers(void)
{
NAN_DATA_LOCK();
for (int i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
forced_memzero(s_nan_ctx.paired_peers[i].nd_pmk,
sizeof(s_nan_ctx.paired_peers[i].nd_pmk));
}
memset(s_nan_ctx.paired_peers, 0, sizeof(s_nan_ctx.paired_peers));
NAN_DATA_UNLOCK();
}
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr)
@@ -575,7 +726,8 @@ void nan_app_post_event(int32_t event_id, void* event_data, size_t event_data_si
g_wifi_osi_funcs._event_post(WIFI_EVENT, event_id, event_data, event_data_size, OSI_FUNCS_TIME_BLOCKING);
}
void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *peer_info)
void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *peer_info,
struct nan_cb_npba_t *npba)
{
if (!peer_info) {
return;
@@ -656,6 +808,19 @@ void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *peer_info
evt->datapath_reqd = (capab & NAN_SDEA_CTRL_DATAPATH_REQD) ? 1 : 0;
evt->ndpe_support = (device_caps & NAN_CAPS_NDPE_ATTR) ? 1 : 0;
evt->security_reqd = (capab & NAN_SDEA_CTRL_SECURITY_REQD) ? 1 : 0;
if (peer_info->peer_security_params) {
const wifi_nan_peer_sdf_security_t *peer_sec = peer_info->peer_security_params;
evt->csid_bitmap = peer_sec->csid_bitmap;
evt->pairing_setup = peer_sec->pairing_setup;
evt->npk_nik_caching = peer_sec->npk_nik_caching;
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
evt->bootstrapping_methods = nan_app_parse_npba_from_publish(npba);
#endif
evt->ssi_version = ssi_ver;
if (ssi && ssi_len) {
if (ssi_ver) {
@@ -712,7 +877,9 @@ void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_info)
os_free(evt);
}
void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info)
void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
uint8_t *shared_key_attr, uint16_t shared_key_attr_buf_len,
struct nan_cb_npba_t *npba)
{
if (!peer_info) {
return;
@@ -722,7 +889,6 @@ void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info)
uint8_t *ssi = peer_info->ssi;
uint16_t ssi_len = peer_info->ssi_len;
uint32_t device_caps = peer_info->device_caps;
uint8_t *shared_key_attr = peer_info->shared_key_attr;
NAN_DATA_LOCK();
if (!nan_find_peer_svc(svc_id, peer_svc_id, peer_mac)) {
@@ -732,11 +898,18 @@ void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info)
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY) && defined(CONFIG_ESP_WIFI_NAN_PAIRING) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
if (shared_key_attr) {
nan_app_receive_pairing_followup(svc_id, peer_svc_id, peer_mac, shared_key_attr);
nan_app_receive_pairing_followup(svc_id, peer_svc_id, peer_mac, shared_key_attr,
shared_key_attr_buf_len);
return;
}
#endif
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
if (npba) {
nan_app_parse_npba_from_receive(svc_id, peer_svc_id, peer_mac, npba);
}
#endif
size_t evt_data_len = sizeof(wifi_event_nan_receive_t) + ssi_len;
wifi_event_nan_receive_t *evt = (wifi_event_nan_receive_t *)os_zalloc(evt_data_len);
if (!evt) {
@@ -1260,8 +1433,6 @@ void esp_nan_action_start(esp_netif_t *nan_netif)
.action_txdone = nan_action_txdone_cb,
.ndp_response_indication = nan_app_ndp_response_indication_cb,
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
.pairing_indication = nan_app_bootstrap_indication_cb,
.pairing_confirm = nan_app_bootstrap_completed_cb,
.get_nira_len = esp_nan_get_nira_len,
.construct_nira = esp_nan_construct_nira,
.receive_pasn = handle_auth_pasn,
@@ -1288,6 +1459,13 @@ void esp_nan_action_stop(void)
s_nan_ctx.state |= NAN_STOPPED_BIT;
NAN_DATA_UNLOCK();
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING) && defined(CONFIG_ESP_WIFI_NAN_SECURITY)
/* ND-PMKs are bound to the local NMI used at PASN time; a later
* sync_start may use a different (randomized) NMI, which would
* invalidate every cached derivation. */
nan_app_clear_paired_peers();
#endif
esp_nan_internal_register_callbacks(NULL);
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);
}
@@ -1447,9 +1625,14 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (!nan_pairing_validate_publish_bootstrapping(publish_cfg->pairing.bootstrapping_methods)) {
ESP_LOGE(TAG, "Invalid bootstrapping methods for Publisher. Only OPPORTUNISTIC and PIN_CODE_DISPLAY allowed");
return 0;
{
uint16_t bootstrap_methods = publish_cfg->pairing ?
publish_cfg->pairing->bootstrapping_methods : 0;
if (!nan_pairing_validate_publish_bootstrapping(bootstrap_methods)) {
ESP_LOGE(TAG, "Invalid bootstrapping methods for Publisher. Only OPPORTUNISTIC and PIN_CODE_DISPLAY allowed");
return 0;
}
}
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
@@ -1523,6 +1706,15 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
goto fail;
}
memcpy(cfg, publish_cfg, sizeof(*cfg));
cfg->pairing = NULL;
if (publish_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, publish_cfg->pairing, sizeof(*cfg->pairing));
}
/* Pre-claim host slot BEFORE calling into the blob. Reason: the blob
* invokes our derive_security_params callback on the WiFi task and looks
@@ -1549,12 +1741,20 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
ESP_LOGI(TAG, "Started Publishing %s [Service ID - %u]", publish_cfg->service_name, pub_id);
nan_finalize_own_svc(publish_cfg->service_name, pub_id, publish_cfg->ndp_resp_needed);
if (cfg->pairing) {
os_free(cfg->pairing);
}
os_free(cfg);
NAN_DATA_UNLOCK();
return pub_id;
fail:
os_free(cfg);
if (cfg) {
if (cfg->pairing) {
os_free(cfg->pairing);
}
os_free(cfg);
}
NAN_DATA_UNLOCK();
return 0;
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
@@ -1593,9 +1793,14 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (!nan_pairing_validate_subscribe_bootstrapping(subscribe_cfg->pairing.bootstrapping_methods)) {
ESP_LOGE(TAG, "Invalid bootstrapping methods for Subscriber. Only OPPORTUNISTIC and PIN_CODE_KEYPAD allowed");
return 0;
{
uint16_t bootstrap_methods = subscribe_cfg->pairing ?
subscribe_cfg->pairing->bootstrapping_methods : 0;
if (!nan_pairing_validate_subscribe_bootstrapping(bootstrap_methods)) {
ESP_LOGE(TAG, "Invalid bootstrapping methods for Subscriber. Only OPPORTUNISTIC and PIN_CODE_KEYPAD allowed");
return 0;
}
}
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
@@ -1665,7 +1870,7 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
goto fail;
}
if (esp_nan_internal_subscribe_service(subscribe_cfg, (uint8_t*) &sub_id, false) != ESP_OK) {
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);
goto fail;
@@ -1899,7 +2104,12 @@ uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req)
ESP_LOGD(TAG, "Requested NDP with "MACSTR" [NDP ID - %d]", MAC2STR(req->peer_mac), ndp_id);
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NDP_ACCEPTED | NDP_REJECTED, pdFALSE, pdFALSE, pdMS_TO_TICKS(NAN_ACTION_TIMEOUT));
/* At worst, Secured NDP (M1..M4) needs ~2.1s; 4*DW races the confirm.
* Pick 6 (~3.1s). Harmless for open NDP - success unblocks instantly;
* only failure path waits longer. */
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NDP_ACCEPTED | NDP_REJECTED,
pdFALSE, pdFALSE,
pdMS_TO_TICKS(6 * NAN_DW_INTVL_MS));
if (bits & NDP_ACCEPTED) {
return ndp_id;
} else if (bits & NDP_REJECTED) {

View File

@@ -159,6 +159,25 @@ enum nan_handshake_state {
/* NIK length for cipher version 0 (Wi-Fi Aware spec v4.0). */
#define NAN_APP_PEER_NIK_LEN 16
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY) && defined(CONFIG_ESP_WIFI_NAN_PAIRING)
/*
* Per-peer pairing record: holds the ND-PMK derived from NM-KDK during the
* PASN/Pairing handshake, together with the cipher that paired NDPs must use.
* Wi-Fi Aware v4.0 §7.6.4.2 — ND-PMK is keyed by (Initiator NMI, Responder NMI)
* and shared across all secured NDPs between the same paired peers, so the
* cache is keyed by peer NMI alone (independent of svc_id).
*/
struct nan_paired_peer {
bool valid;
uint8_t peer_nmi[MACADDR_LEN];
uint8_t role; /* enum nan_role value */
uint8_t ndp_csid; /* WIFI_NAN_CSID_NCS_SK_128 / _256 */
uint8_t nd_pmk[ESP_WIFI_NAN_NDP_PMK_LEN];
uint32_t lifetime_sec;
int64_t established_us;
};
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY && CONFIG_ESP_WIFI_NAN_PAIRING */
/* Per-peer service info */
struct peer_svc_info {
SLIST_ENTRY(peer_svc_info) next;
@@ -275,6 +294,12 @@ typedef struct {
#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT
struct nan_pasn_data *nan_pasn_data;
#endif
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY) && defined(CONFIG_ESP_WIFI_NAN_PAIRING)
/* Pairing records produced by PASN/Pairing key-install. Sized to the
* datapath peer cap since paired peers are the population that can
* actually run an NDP; LRU-evicted on overflow. */
struct nan_paired_peer paired_peers[ESP_WIFI_NAN_DATAPATH_MAX_PEERS];
#endif
} nan_ctx_t;
extern nan_ctx_t s_nan_ctx;
@@ -304,11 +329,15 @@ const uint8_t *nan_pairing_get_null_mac(void);
bool nan_pairing_validate_publish_bootstrapping(uint16_t bootstrapping_methods);
bool nan_pairing_validate_subscribe_bootstrapping(uint16_t bootstrapping_methods);
void nan_app_bootstrap_indication_cb(uint8_t peer_svc_id, uint8_t pub_id,
uint8_t peer_nmi[6], uint16_t selected_method);
void nan_app_bootstrap_completed_cb(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);
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);
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);
bool nan_app_parse_npba_from_receive(uint8_t own_svc_id, uint8_t peer_svc_id,
uint8_t peer_nmi[6], const struct nan_cb_npba_t *npba);
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
@@ -416,9 +445,31 @@ bool nan_security_service_match(const struct own_svc_info *own_svc,
#if CONFIG_ESP_WIFI_NAN_PAIRING
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);
const uint8_t *shared_key_attr,
size_t shared_key_attr_buf_len);
#endif
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/*
* Paired-peer cache API. Called from nan_pairing.c after the PASN install
* callback fires; consumed by the NDP security layer to source ND-PMK + cipher
* for paired peers (no per-service credential involved).
*
* Caller of nan_app_find_paired_peer() must hold NAN_DATA_LOCK while using the
* returned pointer -- it points into s_nan_ctx storage. Same convention as
* nan_find_own_svc() / nan_find_peer_svc().
*/
esp_err_t nan_app_register_paired_peer(const uint8_t *peer_nmi,
uint8_t role,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len,
uint32_t lifetime_sec);
const struct nan_paired_peer *nan_app_find_paired_peer(const uint8_t *peer_nmi);
void nan_app_remove_paired_peer(const uint8_t *peer_nmi);
void nan_app_clear_paired_peers(void);
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
#ifdef __cplusplus
}
#endif

View File

@@ -10,6 +10,7 @@
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
#include <stdint.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_private/wifi.h"
@@ -28,6 +29,11 @@
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
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
struct nan_pasn_data *esp_nan_app_get_pasn_data(void)
{
@@ -39,7 +45,11 @@ 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)
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)
{
wifi_event_nan_pairing_complete_t evt = {0};
@@ -47,6 +57,29 @@ static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi)
return;
}
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
/* Cache ND-PMK for future paired NDPs (Wi-Fi Aware v4.0 §7.6.4.2). The
* NDP cipher (CSID) is determined by the PASN cipher and resolved on the
* supplicant side before this callback fires; if either is missing, the
* pairing event still fires but the security layer will fall back to its
* service-credential path for any subsequent NDP. */
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,
NAN_PAIRING_DEFAULT_NIK_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);
}
#else
(void)role;
(void)ndp_csid;
(void)nd_pmk;
(void)nd_pmk_len;
#endif
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
evt.reason_code = 0;
MACADDR_COPY(evt.peer_nmi, peer_nmi);
@@ -72,8 +105,16 @@ bool nan_pairing_validate_subscribe_bootstrapping(uint16_t bootstrapping_methods
return (bootstrapping_methods & ~valid_methods) == 0;
}
void nan_app_bootstrap_indication_cb(uint8_t peer_svc_id, uint8_t pub_id,
uint8_t peer_nmi[6], uint16_t selected_method)
uint16_t nan_app_parse_npba_from_publish(const struct nan_cb_npba_t *npba)
{
if (!npba || npba->type != WIFI_NAN_NPBA_TYPE_ADVERTISE) {
return 0;
}
return npba->methods;
}
void nan_app_bootstrap_indication(uint8_t peer_svc_id, uint8_t pub_id,
uint8_t peer_nmi[6], uint16_t selected_method)
{
ESP_LOGI(TAG, "Pairing Bootstrapping Request from "MACSTR" [pub_id=%d, method=0x%x]",
MAC2STR(peer_nmi), pub_id, selected_method);
@@ -87,9 +128,9 @@ void nan_app_bootstrap_indication_cb(uint8_t peer_svc_id, uint8_t pub_id,
nan_app_post_event(WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, &evt, sizeof(evt));
}
void nan_app_bootstrap_completed_cb(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)
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)
{
ESP_LOGI(TAG, "Pairing Bootstrapping Response from "MACSTR" [sub_id=%d, status=%d, method=0x%x]",
MAC2STR(peer_nmi), sub_id, status, matched_method);
@@ -105,6 +146,26 @@ void nan_app_bootstrap_completed_cb(uint8_t status, uint8_t peer_svc_id, uint8_t
nan_app_post_event(WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, &evt, sizeof(evt));
}
bool nan_app_parse_npba_from_receive(uint8_t own_svc_id, uint8_t peer_svc_id,
uint8_t peer_nmi[6], const struct nan_cb_npba_t *npba)
{
if (!npba || !peer_nmi) {
return false;
}
switch (npba->type) {
case WIFI_NAN_NPBA_TYPE_REQUEST:
nan_app_bootstrap_indication(peer_svc_id, own_svc_id, peer_nmi, npba->methods);
return true;
case WIFI_NAN_NPBA_TYPE_RESPONSE:
nan_app_bootstrap_completed(npba->status, peer_svc_id, own_svc_id, peer_nmi,
npba->methods, 0);
return true;
default:
return false;
}
}
static esp_err_t nan_send_bootstrap_followup(uint8_t inst_id, uint8_t peer_inst_id,
uint8_t *peer_mac,
wifi_nan_pairing_npba_params_t *pairing_npba)
@@ -339,55 +400,6 @@ int esp_nan_construct_nira(uint8_t *frm)
return (int)(p - frm);
}
#define NAN_MME_ELEMENT_ID 0x4C
/* MME: ElementID(1) + Length(1) + KeyID(2) + IPN(6) + MIC(8) = 18 */
#define NAN_MME_LEN 18
uint32_t esp_nan_get_mme_len(void)
{
return NAN_MME_LEN;
}
/**
* @brief Construct dummy Management MIC Element (MME)
*
* Format: Element ID(1) + Length(1) + Key ID(2) + IPN/BIPN(6) + MIC(8)
* Key ID is set to 4, MIC is 8 bytes random (dummy).
*/
int esp_nan_construct_mme(uint8_t *frm, uint32_t ipn)
{
if (!frm) {
return 0;
}
uint8_t *p = frm;
/* Element ID */
*p++ = NAN_MME_ELEMENT_ID;
/* Length: KeyID(2) + IPN(6) + MIC(8) = 16 */
*p++ = 16;
/* Key ID: 4 (LE16) */
*p++ = 4;
*p++ = 0;
/* IPN/BIPN: 6 bytes from ipn parameter (LE, zero-padded upper 2 bytes) */
*p++ = (uint8_t)(ipn & 0xFF);
*p++ = (uint8_t)((ipn >> 8) & 0xFF);
*p++ = (uint8_t)((ipn >> 16) & 0xFF);
*p++ = (uint8_t)((ipn >> 24) & 0xFF);
*p++ = 0;
*p++ = 0;
/* MIC: 8 bytes random (dummy) */
os_get_random(p, 8);
p += 8;
ESP_LOGI(TAG, "Constructed MME (dummy): len=%d, ipn=0x%lx", (int)(p - frm), (unsigned long)ipn);
return (int)(p - frm);
}
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT) && defined(CONFIG_ESP_WIFI_NAN_SECURITY)
#include "crypto/sha256.h"
@@ -400,7 +412,7 @@ int esp_nan_construct_mme(uint8_t *frm, uint32_t ipn)
#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)
#define NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC 86400U
/* NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC is defined at file scope above. */
#define NAN_ATTR_ID_SHARED_KEY_DESC 0x24
#define GSP_SUBATTR_TRANSPORT_PORT 0x00
#define GSP_SUBATTR_INSTANCE_NAME 0x03
@@ -770,10 +782,11 @@ static void nan_app_send_pairing_followup_eloop(void *eloop_data, void *user_dat
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)
const uint8_t *shared_key_attr,
size_t shared_key_attr_buf_len)
{
uint16_t attr_len;
uint16_t total_len;
size_t attr_body_len;
size_t total_len;
uint8_t nik[NAN_APP_PEER_NIK_LEN];
uint8_t cipher_ver = 0;
uint32_t lifetime_sec = 0;
@@ -783,13 +796,23 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
if (!shared_key_attr || !peer_mac) {
return;
}
if (shared_key_attr_buf_len < NAN_PAIRING_FUP_MIN_ATTR_LEN) {
return;
}
if (shared_key_attr[0] != NAN_ATTR_ID_SHARED_KEY_DESC) {
return;
}
attr_len = shared_key_attr[1] | (shared_key_attr[2] << 8);
total_len = attr_len + 3;
if (total_len < NAN_PAIRING_FUP_MIN_ATTR_LEN) {
const uint16_t *attr_body_len_field = (const uint16_t *)&shared_key_attr[1];
attr_body_len = *attr_body_len_field;
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);
return;
}
total_len = attr_body_len + 3;
if (total_len > shared_key_attr_buf_len) {
return;
}
if (nan_pasn_followup_decrypt_keys(shared_key_attr, total_len,
@@ -809,10 +832,12 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
p_peer_svc->has_nik = true;
ESP_LOGI(TAG, "Stored peer NIK from " MACSTR " (cipher_ver=%u, lifetime=%u s)",
MAC2STR(peer_mac), cipher_ver, lifetime_sec);
ESP_LOG_BUFFER_HEXDUMP("NIK", nik, NAN_APP_PEER_NIK_LEN, ESP_LOG_INFO);
}
NAN_DATA_UNLOCK();
if (total_len > SIZE_MAX - sizeof(*ctx)) {
return;
}
alloc_len = sizeof(*ctx) + total_len;
ctx = os_zalloc(alloc_len);
if (!ctx) {

View File

@@ -426,6 +426,37 @@ static struct ndl_info *nan_ndl_claim_for_pub_peer(uint8_t pub_id, const uint8_t
return NULL;
}
/*
* Fill NDL security context from paired-peer cache (produced at PASN pairing
* completion). Caller must hold NAN_DATA_LOCK.
*/
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
static bool nan_security_fill_from_paired_cache(struct ndl_info *ndl, const uint8_t *peer_nmi)
{
const struct nan_paired_peer *paired;
if (!ndl || !peer_nmi) {
return false;
}
paired = nan_app_find_paired_peer(peer_nmi);
if (!paired || !paired->valid) {
return false;
}
if (paired->ndp_csid == 0 || paired->ndp_csid > 15) {
return false;
}
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << paired->ndp_csid);
memcpy(ndl->security_ctx.nd_pmk, paired->nd_pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
ESP_LOGD(TAG, "Loaded ND-PMK from paired-peer cache for peer "MACSTR" (csid=%u)",
MAC2STR(peer_nmi), paired->ndp_csid);
return true;
}
#endif
/*
* Build RSNA Key Descriptor payload (95-byte EAPOL-Key layout, see
* IEEE 802.11-2020 §12.7.2). NAN carries this body inside the NAN
@@ -896,11 +927,23 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
const struct peer_svc_info *peer_svc,
const uint8_t *peer_nmi)
{
const wifi_nan_credential_t *cred = NULL;
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
const struct nan_paired_peer *paired = NULL;
#endif
if (!ndl || !own_svc || !peer_nmi) {
return ESP_FAIL;
}
const wifi_nan_discovery_security_params_t *cfg = &own_svc->user_cfg;
if (cfg->num_credentials == 0) {
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
paired = nan_app_find_paired_peer(peer_nmi);
#endif
if (cfg->num_credentials == 0
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
&& (!paired || !paired->valid)
#endif
) {
return ESP_OK; /* subscriber didn't request encrypted datapath */
}
@@ -913,12 +956,14 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
if (cred_idx >= cfg->num_credentials) {
cred_idx = 0;
}
const wifi_nan_credential_t *cred = &cfg->creds[cred_idx];
ESP_LOGD(TAG, "NDP req security: svc='%s' using cred[%u] for peer "MACSTR
" (matched_idx=%d, num_creds=%u)",
own_svc->svc_name, cred_idx, MAC2STR(peer_nmi),
(matched_idx == NAN_NO_MATCHED_CRED) ? -1 : (int)matched_idx,
cfg->num_credentials);
if (cfg->num_credentials > 0) {
cred = &cfg->creds[cred_idx];
ESP_LOGD(TAG, "NDP req security: svc='%s' using cred[%u] for peer "MACSTR
" (matched_idx=%d, num_creds=%u)",
own_svc->svc_name, cred_idx, MAC2STR(peer_nmi),
(matched_idx == NAN_NO_MATCHED_CRED) ? -1 : (int)matched_idx,
cfg->num_credentials);
}
uint8_t pmk[ESP_WIFI_NAN_NDP_PMK_LEN];
uint8_t service_id[6];
@@ -934,17 +979,35 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
goto cleanup;
}
if (cred->use_pmk) {
memcpy(pmk, cred->pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
} else {
if (cred->csid == 0) {
uint8_t ndp_csid = 0;
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
if (paired && paired->valid && paired->ndp_csid >= WIFI_NAN_CSID_NCS_SK_128 &&
paired->ndp_csid <= WIFI_NAN_CSID_NCS_SK_256) {
memcpy(pmk, paired->nd_pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
ndp_csid = paired->ndp_csid;
ESP_LOGD(TAG, "NDP req security: using paired-peer cached ND-PMK for " MACSTR
" (csid=%u)", MAC2STR(peer_nmi), ndp_csid);
} else
#endif
{
if (!cred) {
ESP_LOGE(TAG, "NDP req security: no credential and no paired cache for peer " MACSTR,
MAC2STR(peer_nmi));
goto cleanup;
}
if (nan_derive_nd_pmk_from_passphrase(cred->passphrase, cred->csid, service_id,
peer_nmi, pmk) != 0) {
ESP_LOGE(TAG, "NDP req security: passphrase->PMK failed");
goto cleanup;
if (cred->use_pmk) {
memcpy(pmk, cred->pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
} else {
if (cred->csid == 0) {
goto cleanup;
}
if (nan_derive_nd_pmk_from_passphrase(cred->passphrase, cred->csid, service_id,
peer_nmi, pmk) != 0) {
ESP_LOGE(TAG, "NDP req security: passphrase->PMK failed");
goto cleanup;
}
}
ndp_csid = cred->csid;
}
uint8_t pair_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN];
@@ -954,7 +1017,7 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
}
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << cred->csid);
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << ndp_csid);
memcpy(ndl->security_ctx.nd_pmk, pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
memcpy(ndl->security_ctx.nd_pmkid, pair_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN);
@@ -1044,8 +1107,6 @@ int esp_nan_construct_scia_ndp_resp(uint8_t *frm, uint8_t ndp_id, const uint8_t
}
ESP_LOGD(TAG, "Constructing SCIA (NDP Response M2): ndp_id=%d, pub_id=%d", ndp_id, ndl->publisher_id);
ESP_LOGD(TAG, "Using pair-specific PMKID from M1 (not Publish PMKID):");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->security_ctx.nd_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN, ESP_LOG_DEBUG);
uint8_t pub_id = ndl->publisher_id;
uint8_t pmkid_one[1][ESP_WIFI_NAN_NDP_PMKID_LEN];
@@ -1131,7 +1192,16 @@ int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t n
bool need_pmk = (ndl->security_ctx.type != WIFI_NAN_SECURITY_ENCRYPTED) ||
(memcmp(ndl->security_ctx.nd_pmk, zero_pmk, ESP_WIFI_NAN_NDP_PMK_LEN) == 0);
if (need_pmk && have_peer_pmkid) {
bool resolved_from_pairing_cache = false;
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
if (need_pmk) {
resolved_from_pairing_cache = nan_security_fill_from_paired_cache(ndl, peer_nmi);
}
#endif
if (resolved_from_pairing_cache) {
ESP_LOGD(TAG, "NDP Resp Key Desc: resolved PMK from paired-peer cache");
} else if (need_pmk && 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;
@@ -1187,20 +1257,6 @@ int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t n
ndl->kek_len = NAN_NCS_SK_128_KEK_LEN;
ndl->tk_len = NAN_NCS_SK_128_TK_LEN;
ndl->ptk_set = 1;
ESP_LOGD(TAG, "=== PTK Derivation Debug (M2 Responder) ===");
ESP_LOGD(TAG, "ND-PMK:");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->security_ctx.nd_pmk, ESP_WIFI_NAN_NDP_PMK_LEN, ESP_LOG_DEBUG);
ESP_LOGD(TAG, "ANonce:");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->anonce, NAN_NONCE_LEN, ESP_LOG_DEBUG);
ESP_LOGD(TAG, "SNonce:");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->snonce, NAN_NONCE_LEN, ESP_LOG_DEBUG);
ESP_LOGD(TAG, "KCK:");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->nd_kck, NAN_NCS_SK_128_KCK_LEN, ESP_LOG_DEBUG);
ESP_LOGD(TAG, "KEK:");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->nd_kek, NAN_NCS_SK_128_KEK_LEN, ESP_LOG_DEBUG);
ESP_LOGD(TAG, "TK:");
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->nd_tk, NAN_NCS_SK_128_TK_LEN, ESP_LOG_DEBUG);
}
uint8_t *p = buf;
@@ -1598,7 +1654,6 @@ esp_err_t esp_nan_parse_publish_security(const uint8_t *attrs, size_t attrs_len,
scia = nan_find_attr(attrs, attrs_len, NAN_ATTR_ID_SECURITY_CONTEXT, &scia_len);
if (scia && scia_len > 0) {
ESP_LOGD(TAG, "Found SCIA in Publish SDF, len=%d", scia_len);
ESP_LOG_BUFFER_HEXDUMP(TAG, scia, scia_len, ESP_LOG_DEBUG);
size_t offset = 0;
while (offset + 4 <= scia_len && security->num_pmkids < NAN_PEER_MAX_PMKIDS) {
@@ -1658,7 +1713,13 @@ 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);
} else {
}
#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");
}
#endif
else {
ESP_LOGW(TAG, "NDP Indication: PMKID validation failed");
}
}

View File

@@ -260,8 +260,16 @@ else()
set(pasn_src "")
endif()
if(CONFIG_ESP_WIFI_NAN_PAIRING)
set(nan_pair_src
"esp_supplicant/src/esp_nan_supplicant.c")
else()
set(nan_pair_src "")
endif()
idf_component_register(SRCS "${srcs}" "${esp_srcs}" "${tls_src}" "${roaming_src}"
"${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}" "${usd_src}" "${pasn_src}"
"${crypto_src}" "${mbo_src}" "${dpp_src}" "${wps_registrar_src}"
"${usd_src}" "${pasn_src}" "${nan_pair_src}"
INCLUDE_DIRS include port/include esp_supplicant/include
PRIV_INCLUDE_DIRS src src/utils src/common esp_supplicant/src src/crypto
../esp_wifi/wifi_apps/roaming_app/include

View File

@@ -27,7 +27,6 @@ extern "C" {
#endif
struct nan_data;
typedef void (*esp_nan_pairing_key_installed_cb_t)(const uint8_t *peer_nmi);
#if CONFIG_ESP_WIFI_PASN_SUPPORT
#ifndef ETH_ALEN
@@ -46,11 +45,33 @@ typedef void (*esp_nan_pairing_key_installed_cb_t)(const uint8_t *peer_nmi);
#define NAN_PASN_NIK_LEN 16
#endif
#ifndef ESP_NAN_ROLE_ENUM_DEFINED
#define ESP_NAN_ROLE_ENUM_DEFINED
enum nan_role {
NAN_ROLE_IDLE = 0,
NAN_ROLE_PAIRING_INITIATOR = 1,
NAN_ROLE_PAIRING_RESPONDER = 2,
};
#endif
/**
* Pairing-complete callback. Fires after the PASN pairwise TK is installed and
* -- if the PASN PTK carried a KDK -- the ND-PMK has been derived
* (Wi-Fi Aware v4.0 section 7.6.4.2: KDF-HASH-256(NM-KDK, "NDP PMK Derivation",
* Pairing Initiator NMI || Pairing Responder NMI)).
*
* @param peer_nmi Peer NMI (6 bytes).
* @param role enum nan_role value for the local device.
* @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.
* @param nd_pmk_len Length of @a nd_pmk (32 when present, 0 otherwise).
*/
typedef void (*esp_nan_pairing_key_installed_cb_t)(const uint8_t *peer_nmi,
uint8_t role,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len);
/**
* Last PASN key material after successful pairing (PMK + flattened PTK KCK|KEK|TK|KDK).

View File

@@ -5,6 +5,10 @@
*/
#pragma once
#include "sdkconfig.h"
#if CONFIG_ESP_WIFI_NAN_PAIRING
#include <stdint.h>
#include <stddef.h>
#include "esp_private/esp_supp_nan.h"
@@ -13,7 +17,7 @@ struct wpabuf;
struct pasn_data;
struct rsn_pmksa_cache;
typedef void (*nan_pasn_pairing_key_installed_cb_t)(const uint8_t *peer_nmi);
typedef esp_nan_pairing_key_installed_cb_t nan_pasn_pairing_key_installed_cb_t;
struct nan_config {
uint8_t dev_addr[ETH_ALEN];
@@ -53,3 +57,5 @@ 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);
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */

View File

@@ -8,9 +8,10 @@
#include "sdkconfig.h"
#include "esp_private/esp_supp_nan.h"
#include "esp_nan_supp_i.h"
#if CONFIG_ESP_WIFI_PASN_SUPPORT
#if CONFIG_ESP_WIFI_NAN_PAIRING
#include "esp_nan_supp_i.h"
#include "utils/includes.h"
#include "utils/common.h"
@@ -54,11 +55,27 @@ int temp = 1;
#define NAN_PASN_AES_WRAP_OVERHEAD 8
#define NAN_PASN_AES_WRAP_MIN_CIPHERTEXT (NAN_PASN_AES_WRAP_OVERHEAD + 8)
/**
* 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
* inherits from the PASN cipher).
*
* Returns 0 when the PASN cipher has no SK equivalent.
*/
static uint8_t nan_pasn_pasn_cipher_to_ndp_csid(int pasn_cipher)
{
switch (pasn_cipher) {
case WPA_CIPHER_CCMP: return WIFI_NAN_CSID_NCS_SK_128;
case WPA_CIPHER_GCMP_256: return WIFI_NAN_CSID_NCS_SK_256;
default: return 0;
}
}
/**
* Key lengths for NCS-PK-PASN-128 / NCS-PK-PASN-256 (Wi-Fi Aware pairing).
* Mirrors hostap @c nan_crypto_cipher_*_len in src/nan/nan_crypto.c.
*/
static size_t nan_pasn_cipher_kck_len(int cipher)
static size_t __attribute__((unused)) nan_pasn_cipher_kck_len(int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP:
@@ -239,8 +256,7 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
return -1;
}
ESP_LOG_BUFFER_HEXDUMP("NAN PASN: NM-TK",
ptk->tk, ptk->tk_len, ESP_LOG_INFO);
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),
ptk->tk, ptk->tk_len,
@@ -541,25 +557,17 @@ static int nan_pasn_parse_nik_kdes(const u8 *data, size_t len, int cipher,
} else if (gtk_key_len && selector == sel_igtk &&
kde_body_len >= gtk_kde_min) {
/* IGTK KDE: KeyID(2 LE) | IPN(6) | IGTK */
size_t igtk_len = kde_body_len - 8;
wpa_printf(MSG_DEBUG,
"NAN: IGTK KDE KeyID=%u igtk_len=%zu",
WPA_GET_LE16(kde_body), igtk_len);
wpa_printf(MSG_DEBUG, "NAN: IGTK KDE KeyID=%u igtk_len=%zu",
WPA_GET_LE16(kde_body), kde_body_len - 8);
wpa_hexdump(MSG_DEBUG, "NAN: IGTK IPN", kde_body + 2, 6);
wpa_hexdump_key(MSG_DEBUG, "NAN: IGTK",
kde_body + 8, igtk_len);
ESP_LOG_BUFFER_HEXDUMP("IGTK", kde_body + 8, igtk_len, ESP_LOG_INFO);
wpa_hexdump_key(MSG_DEBUG, "NAN: IGTK", kde_body + 8, kde_body_len - 8);
} else if (gtk_key_len && selector == sel_bigtk &&
kde_body_len >= gtk_kde_min) {
/* BIGTK KDE: KeyID(2 LE) | BIPN(6) | BIGTK */
size_t bigtk_len = kde_body_len - 8;
wpa_printf(MSG_DEBUG,
"NAN: BIGTK KDE KeyID=%u bigtk_len=%zu",
WPA_GET_LE16(kde_body), bigtk_len);
wpa_printf(MSG_DEBUG, "NAN: BIGTK KDE KeyID=%u bigtk_len=%zu",
WPA_GET_LE16(kde_body), kde_body_len - 8);
wpa_hexdump(MSG_DEBUG, "NAN: BIPN", kde_body + 2, 6);
wpa_hexdump_key(MSG_DEBUG, "NAN: BIGTK",
kde_body + 8, bigtk_len);
ESP_LOG_BUFFER_HEXDUMP("BIGTK", kde_body + 8, bigtk_len, ESP_LOG_INFO);
wpa_hexdump_key(MSG_DEBUG, "NAN: BIGTK", kde_body + 8, kde_body_len - 8);
}
pos += 2 + elen;
@@ -708,7 +716,6 @@ int nan_pasn_followup_decrypt_keys(const uint8_t *shared_key_attr,
return -1;
}
ESP_LOG_BUFFER_HEXDUMP("Key Data", wpabuf_head(key_data), wpabuf_len(key_data), ESP_LOG_INFO);
if (nan_pasn_parse_nik_kdes(wpabuf_head(key_data), wpabuf_len(key_data),
saved->cipher, nik, &found_cipher_ver,
&found_lifetime, &lifetime_bitmap) != 0) {
@@ -764,20 +771,6 @@ static int nan_pasn_get_current_freq_mhz(void)
return nan_chan_to_freq_mhz(primary);
}
static const char * __attribute__((unused))
nan_pasn_role_to_str(enum nan_role role)
{
switch (role) {
case NAN_ROLE_PAIRING_INITIATOR:
return "initiator";
case NAN_ROLE_PAIRING_RESPONDER:
return "responder";
case NAN_ROLE_IDLE:
default:
return "idle";
}
}
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)
@@ -790,7 +783,9 @@ static void nan_pasn_auth_timeout_cb(void *eloop_ctx, void *user_data)
wpa_printf(MSG_INFO,
"NAN PASN: %s auth timed out after %u seconds",
nan_pasn_role_to_str((enum nan_role)(uintptr_t)user_data),
((enum nan_role)(uintptr_t)user_data == NAN_ROLE_PAIRING_INITIATOR) ?
"initiator" :
"responder",
NAN_PASN_AUTH_TIMEOUT_SECS);
nan_pasn_data_deinit(nan);
}
@@ -1253,7 +1248,13 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
nan_pasn_copy_keys_from_pasn(nan, pasn);
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
nan->pairing_key_installed_cb) {
nan->pairing_key_installed_cb(pasn->peer_addr);
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_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
nd_pmk, nd_pmk_len);
}
forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
nan_pasn_data_deinit(nan);
@@ -1304,7 +1305,13 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
nan_pasn_copy_keys_from_pasn(nan, pasn);
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
nan->pairing_key_installed_cb) {
nan->pairing_key_installed_cb(pasn->peer_addr);
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_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
nd_pmk, nd_pmk_len);
}
}
#ifdef CONFIG_TESTING_OPTIONS
@@ -1809,141 +1816,4 @@ int esp_nan_supp_pasn_responder_init(const uint8_t *peer_nmi, uint32_t pincode,
return 0;
}
#else /* !CONFIG_ESP_WIFI_PASN_SUPPORT */
void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status)
{
(void)buf;
(void)len;
(void)trans_seq;
(void)status;
}
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr,
int freq, int role, const uint8_t *bssid,
const uint8_t *ssid, size_t ssid_len)
{
(void)pd;
(void)peer_addr;
(void)freq;
(void)role;
(void)bssid;
(void)ssid;
(void)ssid_len;
return -1;
}
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr, int freq)
{
(void)pd;
(void)addr;
(void)freq;
return -1;
}
struct nan_pasn_data *nan_pasn_data_init(void)
{
return NULL;
}
void nan_pasn_data_deinit(struct nan_pasn_data *pd)
{
(void)pd;
}
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq)
{
(void)pd;
(void)peer_addr;
(void)freq;
return -1;
}
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr, int freq)
{
(void)pd_out;
(void)peer_addr;
(void)freq;
return -1;
}
int nan_pasn_auth_eloop(const uint8_t *peer_addr, uint32_t pincode)
{
(void)peer_addr;
(void)pincode;
return -1;
}
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)
{
(void)secs;
(void)usecs;
(void)peer_addr;
(void)freq;
(void)role;
(void)bssid;
(void)ssid;
(void)ssid_len;
return -1;
}
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
{
(void)peer_addr;
(void)pincode;
return -1;
}
int pasn_responder_init_eloop(const uint8_t *peer_addr, uint32_t pincode)
{
(void)peer_addr;
(void)pincode;
return -1;
}
int esp_nan_supp_pasn_responder_init(const uint8_t *peer_nmi, uint32_t pincode,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
{
(void)peer_nmi;
(void)pincode;
(void)pairing_key_installed_cb;
return -1;
}
int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
{
(void)peer_nmi;
(void)pincode;
(void)pairing_key_installed_cb;
return -1;
}
const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void)
{
return NULL;
}
void nan_pasn_clear_saved_keys(void)
{
}
int nan_pasn_followup_decrypt_keys(const uint8_t *shared_key_attr,
size_t attr_total_len,
uint8_t *nik, size_t nik_size,
uint8_t *cipher_ver,
uint32_t *lifetime_sec)
{
(void)shared_key_attr;
(void)attr_total_len;
(void)nik;
(void)nik_size;
(void)cipher_ver;
(void)lifetime_sec;
return -1;
}
#endif /* CONFIG_ESP_WIFI_PASN_SUPPORT */
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */

View File

@@ -333,7 +333,6 @@ void esp_wifi_set_sigma_internal(bool flag);
void esp_wifi_ap_set_group_mgmt_cipher_internal(wifi_cipher_type_t cipher);
uint8_t esp_wifi_op_class_supported_internal(uint8_t op_class, uint8_t min_chan, uint8_t max_chan, uint8_t inc, uint8_t bw, channel_bitmap_t *non_pref_channels);
bool esp_wifi_is_wpa3_compatible_mode_enabled(uint8_t if_index);
esp_err_t esp_wifi_nan_get_pasn_attr(uint8_t *buf, size_t buf_len, size_t *actual_len);
uint8_t esp_wifi_ap_get_owe_config_internal(void);
#endif /* _ESP_WIFI_DRIVER_H_ */

View File

@@ -66,6 +66,7 @@ void pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
e->next = entry->next;
break;
}
e = e->next;
}
if (!e) {
@@ -394,6 +395,8 @@ struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *aa)
{
if (!pmksa)
return NULL;
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));