diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index c6885320dfa..f593d696164 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -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" diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index 20eca9d65b4..a8eb783f9a3 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -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. * diff --git a/components/esp_wifi/include/esp_wifi_types_generic.h b/components/esp_wifi/include/esp_wifi_types_generic.h index 1d79e0658c4..709d55e4e4b 100644 --- a/components/esp_wifi/include/esp_wifi_types_generic.h +++ b/components/esp_wifi/include/esp_wifi_types_generic.h @@ -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 change,doesn'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) */ diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index e61a56b9e6d..b83b04d03d5 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit e61a56b9e6d1a522a2f47a43ef4e7db1684c6825 +Subproject commit b83b04d03d5645fcf063ef8a6101bf48cc2c38e6 diff --git a/components/esp_wifi/remote/Kconfig.wifi.in b/components/esp_wifi/remote/Kconfig.wifi.in index ffbde47764c..f0482f1c2cb 100644 --- a/components/esp_wifi/remote/Kconfig.wifi.in +++ b/components/esp_wifi/remote/Kconfig.wifi.in @@ -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" diff --git a/components/esp_wifi/remote/Kconfig.wifi_is_remote.in b/components/esp_wifi/remote/Kconfig.wifi_is_remote.in index f19740e6f8b..64b1e119b62 100644 --- a/components/esp_wifi/remote/Kconfig.wifi_is_remote.in +++ b/components/esp_wifi/remote/Kconfig.wifi_is_remote.in @@ -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 diff --git a/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h b/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h index 3b809654f39..45eea50f89f 100644 --- a/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h +++ b/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h @@ -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 change,doesn'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) */ diff --git a/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h index c3862e92e7f..df4b1fac425 100644 --- a/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h +++ b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h @@ -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. * diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c index 1b280353cc7..a8a137ad20e 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c @@ -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) { diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h index 622d9234796..80064086d8a 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h @@ -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 diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c index 1fdf665521e..53c26be1959 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c @@ -10,6 +10,7 @@ #if defined(CONFIG_ESP_WIFI_NAN_PAIRING) +#include #include #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) { diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c index 9698d340d82..fa408b91742 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c @@ -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"); } } diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index aa981ef0e65..8a9b5829334 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -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 diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h b/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h index fd68e17a371..1ab35e54cfa 100644 --- a/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h +++ b/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h @@ -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). diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h index 61bb80cf7c0..92accd57134 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h @@ -5,6 +5,10 @@ */ #pragma once +#include "sdkconfig.h" + +#if CONFIG_ESP_WIFI_NAN_PAIRING + #include #include #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 */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c index dfc9cd3da28..3a3ea9d2edd 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c @@ -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 */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h index 93c7de167f8..dcf3fed52f7 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h @@ -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_ */ diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c index 54707f6bcbf..0914de2e7bd 100644 --- a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c @@ -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));