mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
Merge branch 'fix/backport_nan_fixes_v6.1' into 'release/v6.1'
Backport NAN features and a few fixes to v6.1 (Backport v6.1) See merge request espressif/esp-idf!50349
This commit is contained in:
@@ -862,6 +862,43 @@ int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]);
|
||||
*/
|
||||
int esp_netif_get_all_preferred_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]);
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
/**
|
||||
* @brief Add or update a static (permanent) IPv6 neighbor cache entry
|
||||
*
|
||||
* Installs a fixed IPv6 -> link-layer address mapping that bypasses Neighbor
|
||||
* Discovery: no Neighbor Solicitation/Advertisement is exchanged for this
|
||||
* address, the entry never ages out and is never overwritten by incoming
|
||||
* advertisements. Calling again with the same address updates the mapping.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[in] addr Neighbor's IPv6 address
|
||||
* @param[in] mac Neighbor's link-layer address (interface hwaddr_len bytes)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS on invalid arguments
|
||||
* - ESP_FAIL if the entry could not be installed (e.g. neighbor cache full)
|
||||
*/
|
||||
esp_err_t esp_netif_add_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, const uint8_t *mac);
|
||||
|
||||
/**
|
||||
* @brief Remove a static IPv6 neighbor cache entry
|
||||
*
|
||||
* Removes an entry previously added with esp_netif_add_static_neighbor().
|
||||
* Dynamic (non-static) entries are left untouched.
|
||||
*
|
||||
* @param[in] esp_netif Handle to esp-netif instance
|
||||
* @param[in] addr Neighbor's IPv6 address
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS on invalid arguments
|
||||
* - ESP_FAIL if no matching static entry exists
|
||||
*/
|
||||
esp_err_t esp_netif_remove_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr);
|
||||
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
/**
|
||||
* @brief Cause the TCP/IP stack to add an IPv6 address to the interface
|
||||
*
|
||||
|
||||
@@ -2478,6 +2478,56 @@ int esp_netif_get_all_preferred_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip
|
||||
}
|
||||
return addr_count;
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
typedef struct {
|
||||
const esp_ip6_addr_t *addr;
|
||||
const uint8_t *mac;
|
||||
} esp_netif_static_neighbor_t;
|
||||
|
||||
static esp_err_t esp_netif_add_static_neighbor_api(esp_netif_api_msg_t *msg)
|
||||
{
|
||||
esp_netif_t *esp_netif = msg->esp_netif;
|
||||
const esp_netif_static_neighbor_t *nbr = msg->data;
|
||||
ip6_addr_t ip6;
|
||||
|
||||
memcpy(&ip6, nbr->addr, sizeof(ip6_addr_t));
|
||||
if (nd6_add_static_neighbor(esp_netif->lwip_netif, &ip6, nbr->mac) != ERR_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_add_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, const uint8_t *mac)
|
||||
{
|
||||
if (esp_netif == NULL || esp_netif->lwip_netif == NULL || addr == NULL || mac == NULL) {
|
||||
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
|
||||
}
|
||||
esp_netif_static_neighbor_t nbr = { .addr = addr, .mac = mac };
|
||||
return esp_netif_lwip_ipc_call(esp_netif_add_static_neighbor_api, esp_netif, &nbr);
|
||||
}
|
||||
|
||||
static esp_err_t esp_netif_remove_static_neighbor_api(esp_netif_api_msg_t *msg)
|
||||
{
|
||||
esp_netif_t *esp_netif = msg->esp_netif;
|
||||
const esp_ip6_addr_t *addr = msg->data;
|
||||
ip6_addr_t ip6;
|
||||
|
||||
memcpy(&ip6, addr, sizeof(ip6_addr_t));
|
||||
if (nd6_remove_static_neighbor(esp_netif->lwip_netif, &ip6) != ERR_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_netif_remove_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr)
|
||||
{
|
||||
if (esp_netif == NULL || esp_netif->lwip_netif == NULL || addr == NULL) {
|
||||
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
|
||||
}
|
||||
return esp_netif_lwip_ipc_call(esp_netif_remove_static_neighbor_api, esp_netif, (void *)addr);
|
||||
}
|
||||
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||
#endif
|
||||
|
||||
esp_netif_flags_t esp_netif_get_flags(esp_netif_t *esp_netif)
|
||||
|
||||
@@ -67,8 +67,8 @@ typedef struct {
|
||||
uint16_t csid_bitmap; /**< Selected Cipher Suite ID bit (WIFI_NAN_CSID_BIT_*) */
|
||||
uint8_t nd_pmk[ESP_WIFI_NAN_NDP_PMK_LEN]; /**< ND-PMK */
|
||||
uint8_t nd_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< ND-PMKID */
|
||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
|
||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
|
||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
|
||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
|
||||
uint8_t reserved: 6; /**< Reserved */
|
||||
} wifi_nan_security_params_t;
|
||||
|
||||
@@ -89,10 +89,11 @@ typedef struct {
|
||||
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 group_mgmt_prot: 1; /**< Peer advertises IGTKSA (CSIA caps bits 1-2 != 0) */
|
||||
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 */
|
||||
uint8_t group_bigtk_prot: 1; /**< Peer advertises BIGTKSA (CSIA caps bits 1-2 == 10) */
|
||||
uint8_t reserved: 3; /**< Reserved */
|
||||
} wifi_nan_peer_sdf_security_t;
|
||||
|
||||
/* NAN Peer info parsed from SDF */
|
||||
@@ -106,6 +107,7 @@ struct nan_cb_peer_info {
|
||||
uint16_t ssi_len; /**< SSI length in bytes */
|
||||
wifi_nan_peer_sdf_security_t *peer_security_params; /**< Peer's discovery security params parsed from SDF */
|
||||
nan_vendor_ie_t *vendor_ie; /**< Vendor-specific IE, if any */
|
||||
bool nira_verified; /**< true when received NIRA tag verified against cached NIK */
|
||||
};
|
||||
|
||||
/* NDP Peer info parsed from NAF. */
|
||||
@@ -196,6 +198,7 @@ struct nan_sync_callbacks {
|
||||
uint32_t (* get_nira_len)(void);
|
||||
int (* construct_nira)(uint8_t *frm);
|
||||
bool (*verify_nira)(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
|
||||
bool (*peer_nik_cached)(uint8_t *peer_mac);
|
||||
};
|
||||
|
||||
/* Host helpers for NAN encrypted-datapath, registered via
|
||||
@@ -228,9 +231,11 @@ struct nan_secure_dp_funcs {
|
||||
* with the given Key Data field length. */
|
||||
uint32_t (*get_shared_key_desc_attr_len)(uint16_t key_data_len);
|
||||
|
||||
/* Byte length of the M4 Shared Key Descriptor including any
|
||||
* encrypted KDE payload (GTK/IGTK/BIGTK). */
|
||||
int (*ndp_security_install_get_shared_desc_len)(void);
|
||||
/* Exact byte length of the M4 (NDP Security Install) Shared Key Descriptor
|
||||
* attribute for this NDP, including any encrypted KDE payload (GTK/IGTK/BIGTK).
|
||||
* @ndp_id + @peer_nmi identify the NDL so the host returns the exact length the
|
||||
* builder will write (no over-reservation / on-air zero-padding). */
|
||||
int (*ndp_security_install_get_shared_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
|
||||
uint8_t (*get_ndp_resp_num_pmkids)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
uint32_t (*get_ndp_resp_shared_key_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
@@ -360,10 +365,21 @@ struct nan_secure_dp_funcs {
|
||||
const wifi_nan_discovery_security_params_t *sec_cfg,
|
||||
wifi_nan_security_params_t *out_derived);
|
||||
|
||||
/* Returns the cipher-suite bitmap negotiated for an in-flight NDP,
|
||||
* or 0 if the NDP is open. Used by RX/TX paths to decide whether
|
||||
* to apply CCMP/GCMP and which key length to use. */
|
||||
/* Returns the full cipher-suite bitmap negotiated for an in-flight NDP
|
||||
* (including the group cipher NCS-GTK when group-addressed data protection
|
||||
* is in use), or 0 if the NDP is open. The blob treats this as an opaque
|
||||
* value passed straight to the host CSIA callbacks (construct_csia /
|
||||
* get_csia_len) to build the on-air M1/M3 CSIA own-bitmap; it must NOT
|
||||
* interpret individual CSID bits. Pairwise cipher selection and key install
|
||||
* are host-driven and independent of this value. */
|
||||
uint16_t (*get_ndp_security_csid)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
|
||||
/* Exact byte length of the M3 (NDP Confirm) Shared Key Descriptor attribute for
|
||||
* this NDP, including any encrypted KDE payload (GTK/IGTK/BIGTK). @ndp_id +
|
||||
* @peer_nmi identify the NDL. Mirrors ndp_security_install_get_shared_desc_len
|
||||
* for the initiator path. Appended at the end of the struct to preserve the
|
||||
* layout of the fields above. */
|
||||
int (*ndp_confirm_get_shared_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1193,7 +1209,7 @@ esp_err_t esp_wifi_disconnect_internal(void);
|
||||
uint32_t esp_nan_get_nira_len(void);
|
||||
|
||||
/**
|
||||
* @brief Construct dummy NAN Identity Resolution Attribute (NIRA)
|
||||
* @brief Construct NAN Identity Resolution Attribute (NIRA)
|
||||
*
|
||||
* @param[out] frm Buffer to write the attribute to
|
||||
*
|
||||
@@ -1201,6 +1217,19 @@ uint32_t esp_nan_get_nira_len(void);
|
||||
*/
|
||||
int esp_nan_construct_nira(uint8_t *frm);
|
||||
|
||||
/**
|
||||
* @brief Construct a NAN Cipher Suite Info Attribute (CSIA)
|
||||
*
|
||||
* @param[out] frm Buffer to write the attribute to
|
||||
* @param[in] pub_id Publish service instance id
|
||||
* @param[in] own_csid_bitmap Locally supported cipher suite bitmap
|
||||
* @param[in] peer_csid_bitmap Peer cipher suite bitmap, or 0 to use own bitmap
|
||||
*
|
||||
* @return Number of bytes written, or 0 on failure/no cipher suite
|
||||
*/
|
||||
int esp_nan_construct_csia(uint8_t *frm, uint8_t pub_id,
|
||||
uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
|
||||
|
||||
/**
|
||||
* @brief Verify a received NAN Identity Resolution Attribute (NIRA)
|
||||
*
|
||||
@@ -1212,6 +1241,24 @@ int esp_nan_construct_nira(uint8_t *frm);
|
||||
*/
|
||||
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
|
||||
|
||||
/**
|
||||
* @brief Verify a received NIRA and resolve the matched own service id
|
||||
*
|
||||
* Behaves like @ref esp_nan_verify_nira but additionally outputs the local
|
||||
* service instance id the verifying NIK maps to, used to anchor a pairing
|
||||
* verify-session flag. @p own_inst_id is set to 0 when the identity does not
|
||||
* resolve to an active local service.
|
||||
*
|
||||
* @param[in] peer_mac NMI of the sender
|
||||
* @param[in] nira_attr NIRA attribute buffer
|
||||
* @param[in] nira_attr_len Attribute length in bytes
|
||||
* @param[out] own_inst_id Resolved own service instance id (0 if none)
|
||||
*
|
||||
* @return true if the tag matches, false otherwise
|
||||
*/
|
||||
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
|
||||
uint16_t nira_attr_len, uint8_t *own_inst_id);
|
||||
|
||||
/**
|
||||
* @brief Get the time information from the MAC clock. The time is precise only if modem sleep or light sleep is not enabled.
|
||||
*
|
||||
|
||||
@@ -81,6 +81,42 @@ bool esp_wifi_is_if_ready_when_started(wifi_netif_driver_t ifx);
|
||||
*/
|
||||
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
|
||||
|
||||
/**
|
||||
* @brief Derive an IPv6 link-local address from a link-layer (MAC) address
|
||||
*
|
||||
* Computes fe80::/64 combined with the EUI-64 form of the given MAC (the 802
|
||||
* group bit complemented) into an esp_ip6_addr_t (zone 0). Interface-agnostic.
|
||||
*
|
||||
* @param[out] ip6 destination, set to the derived IPv6 link-local address
|
||||
* @param[in] mac source link-layer (MAC) address (6 bytes)
|
||||
*/
|
||||
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6]);
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
/**
|
||||
* @brief Pin (or remove) a static IPv6 link-local neighbor mapping on a wifi netif
|
||||
*
|
||||
* Installs a fixed link-local IPv6 -> MAC mapping for a peer reachable on the
|
||||
* given wifi interface so that traffic to the peer bypasses Neighbor Discovery
|
||||
* (no NS/NA exchanged), or removes a previously installed one. This layer owns
|
||||
* both the netif lookup (from the interface type) and the derivation of the
|
||||
* peer's link-local address from its MAC, so the caller only supplies the
|
||||
* interface and the peer MAC. Only available when lwIP static ND6 entries are
|
||||
* enabled.
|
||||
*
|
||||
* @param[in] wifi_if wifi interface the peer is reachable on
|
||||
* @param[in] mac peer's link-layer (MAC) address
|
||||
* @param[in] add true to add the mapping, false to remove it
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if mac is NULL or wifi_if is out of range
|
||||
* - ESP_ERR_INVALID_STATE if the interface's netif is not up
|
||||
* - error code from the underlying esp_netif call otherwise
|
||||
*/
|
||||
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add);
|
||||
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -606,6 +606,7 @@ typedef struct {
|
||||
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
||||
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
||||
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
||||
bool group_mgmt_prot; /**< Device-global group management protection (IGTKSA/BIGTKSA): BIP-protect Beacons + multicast SDFs. Forces GTKSA on all secured services (CSIA caps cannot encode IGTK/BIGTK without GTKSA). */
|
||||
} wifi_nan_sync_config_t;
|
||||
|
||||
/**
|
||||
@@ -932,9 +933,9 @@ typedef enum {
|
||||
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5,
|
||||
WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6,
|
||||
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_GTK_CCMP_128 = 5, /**< NCS-GTK-CCMP-128, the group-data cipher (GTKSA). Selected internally when group_data_prot is set; not user-selectable via csid_bitmap. */
|
||||
WIFI_NAN_CSID_NCS_GTK_GCMP_256 = 6, /**< NCS-GTK-GCMP-256. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128 (NAN Pairing). Requires CONFIG_ESP_WIFI_NAN_PAIRING and the Wi-Fi Aware component (esp-wifi-apps); not usable with stand-alone ESP-IDF. */
|
||||
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
|
||||
} wifi_nan_cipher_suite_id_t;
|
||||
|
||||
@@ -942,8 +943,8 @@ typedef enum {
|
||||
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCM_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCM_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCM_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCM_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCMP_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCMP_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
|
||||
|
||||
@@ -974,8 +975,8 @@ typedef struct {
|
||||
* is computed by the stack as the union of each credential's @c csid.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
|
||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
|
||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
|
||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
|
||||
uint8_t reserved: 6; /**< Reserved */
|
||||
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
|
||||
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
|
||||
@@ -1305,8 +1306,6 @@ typedef enum {
|
||||
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
|
||||
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
|
||||
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
|
||||
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
|
||||
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN pairing completed after NIK follow-up exchange */
|
||||
WIFI_EVENT_NAN_CLUSTER_JOIN, /**< Posted when the device joins, starts, or merges into a NAN cluster */
|
||||
@@ -1637,38 +1636,6 @@ typedef struct {
|
||||
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
|
||||
} wifi_event_ndp_terminated_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Request is received from a peer.
|
||||
* The application should respond using esp_wifi_nan_bootstrap_response().
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t selected_method; /**< Bootstrapping method selected by initiator (one WIFI_NAN_BOOTSTRAP_* bit) */
|
||||
uint8_t is_comeback; /**< 1 if this is a comeback retry with cookie */
|
||||
uint32_t cookie; /**< Comeback cookie from initiator (0 if none) */
|
||||
} wifi_event_nan_bootstrap_indication_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Response is received,
|
||||
* or when the bootstrapping handshake completes/fails.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t status; /**< 0=Accepted, 1=Rejected, 2=Comeback (wifi_nan_pairing_status_t) */
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
|
||||
uint8_t reason_code; /**< Rejection reason (valid if rejected) */
|
||||
uint16_t comeback_after; /**< Comeback deferral time in TUs (valid if comeback) */
|
||||
uint32_t cookie; /**< Comeback cookie from responder (0 if none) */
|
||||
} wifi_event_nan_bootstrap_complete_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
|
||||
*/
|
||||
|
||||
+1
-1
Submodule components/esp_wifi/lib updated: 9ff50d7c69...c9950ae98a
@@ -81,6 +81,42 @@ bool esp_wifi_is_if_ready_when_started(wifi_netif_driver_t ifx);
|
||||
*/
|
||||
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
|
||||
|
||||
/**
|
||||
* @brief Derive an IPv6 link-local address from a link-layer (MAC) address
|
||||
*
|
||||
* Computes fe80::/64 combined with the EUI-64 form of the given MAC (the 802
|
||||
* group bit complemented) into an esp_ip6_addr_t (zone 0). Interface-agnostic.
|
||||
*
|
||||
* @param[out] ip6 destination, set to the derived IPv6 link-local address
|
||||
* @param[in] mac source link-layer (MAC) address (6 bytes)
|
||||
*/
|
||||
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6]);
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
/**
|
||||
* @brief Pin (or remove) a static IPv6 link-local neighbor mapping on a wifi netif
|
||||
*
|
||||
* Installs a fixed link-local IPv6 -> MAC mapping for a peer reachable on the
|
||||
* given wifi interface so that traffic to the peer bypasses Neighbor Discovery
|
||||
* (no NS/NA exchanged), or removes a previously installed one. This layer owns
|
||||
* both the netif lookup (from the interface type) and the derivation of the
|
||||
* peer's link-local address from its MAC, so the caller only supplies the
|
||||
* interface and the peer MAC. Only available when lwIP static ND6 entries are
|
||||
* enabled.
|
||||
*
|
||||
* @param[in] wifi_if wifi interface the peer is reachable on
|
||||
* @param[in] mac peer's link-layer (MAC) address
|
||||
* @param[in] add true to add the mapping, false to remove it
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if mac is NULL or wifi_if is out of range
|
||||
* - ESP_ERR_INVALID_STATE if the interface's netif is not up
|
||||
* - error code from the underlying esp_netif call otherwise
|
||||
*/
|
||||
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add);
|
||||
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -606,6 +606,7 @@ typedef struct {
|
||||
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
||||
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
||||
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
||||
bool group_mgmt_prot; /**< Device-global group management protection (IGTKSA/BIGTKSA): BIP-protect Beacons + multicast SDFs. Forces GTKSA on all secured services (CSIA caps cannot encode IGTK/BIGTK without GTKSA). */
|
||||
} wifi_nan_sync_config_t;
|
||||
|
||||
/**
|
||||
@@ -932,9 +933,9 @@ typedef enum {
|
||||
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5,
|
||||
WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6,
|
||||
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_GTK_CCMP_128 = 5, /**< NCS-GTK-CCMP-128, the group-data cipher (GTKSA). Selected internally when group_data_prot is set; not user-selectable via csid_bitmap. */
|
||||
WIFI_NAN_CSID_NCS_GTK_GCMP_256 = 6, /**< NCS-GTK-GCMP-256. Reserved: not supported right now. */
|
||||
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128 (NAN Pairing). Requires CONFIG_WIFI_RMT_NAN_PAIRING and the Wi-Fi Aware component (esp-wifi-apps); not usable with stand-alone ESP-IDF. */
|
||||
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
|
||||
} wifi_nan_cipher_suite_id_t;
|
||||
|
||||
@@ -942,8 +943,8 @@ typedef enum {
|
||||
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCM_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCM_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCM_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCM_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCMP_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCMP_256)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
|
||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
|
||||
|
||||
@@ -974,8 +975,8 @@ typedef struct {
|
||||
* is computed by the stack as the union of each credential's @c csid.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
|
||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
|
||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
|
||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
|
||||
uint8_t reserved: 6; /**< Reserved */
|
||||
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
|
||||
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
|
||||
@@ -1305,8 +1306,6 @@ typedef enum {
|
||||
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
|
||||
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
|
||||
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
|
||||
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
|
||||
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
|
||||
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN pairing completed after NIK follow-up exchange */
|
||||
WIFI_EVENT_NAN_CLUSTER_JOIN, /**< Posted when the device joins, starts, or merges into a NAN cluster */
|
||||
@@ -1637,38 +1636,6 @@ typedef struct {
|
||||
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
|
||||
} wifi_event_ndp_terminated_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Request is received from a peer.
|
||||
* The application should respond using esp_wifi_nan_bootstrap_response().
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t selected_method; /**< Bootstrapping method selected by initiator (one WIFI_NAN_BOOTSTRAP_* bit) */
|
||||
uint8_t is_comeback; /**< 1 if this is a comeback retry with cookie */
|
||||
uint32_t cookie; /**< Comeback cookie from initiator (0 if none) */
|
||||
} wifi_event_nan_bootstrap_indication_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED event
|
||||
*
|
||||
* Posted when a NAN Pairing Bootstrapping Response is received,
|
||||
* or when the bootstrapping handshake completes/fails.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t status; /**< 0=Accepted, 1=Rejected, 2=Comeback (wifi_nan_pairing_status_t) */
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
|
||||
uint8_t reason_code; /**< Rejection reason (valid if rejected) */
|
||||
uint16_t comeback_after; /**< Comeback deferral time in TUs (valid if comeback) */
|
||||
uint32_t cookie; /**< Comeback cookie from responder (0 if none) */
|
||||
} wifi_event_nan_bootstrap_complete_t;
|
||||
|
||||
/**
|
||||
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -184,6 +184,11 @@ static void wifi_default_action_nan_started(void *arg, esp_event_base_t base, in
|
||||
if (s_wifi_netifs[WIFI_IF_NAN] != NULL) {
|
||||
wifi_start(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data);
|
||||
esp_nan_action_start(s_wifi_netifs[WIFI_IF_NAN]);
|
||||
/* Bring the netif up before esp_netif_create_ip6_linklocal() (a no-op unless
|
||||
* netif_is_up()). esp_netif_up() is private, so use the public action handler;
|
||||
* NAN is non-DHCP, so it only calls esp_netif_up() and ignores the event args. */
|
||||
esp_netif_action_connected(s_wifi_netifs[WIFI_IF_NAN], NULL, 0, NULL);
|
||||
esp_netif_create_ip6_linklocal(s_wifi_netifs[WIFI_IF_NAN]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_log.h"
|
||||
@@ -181,3 +182,45 @@ esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6])
|
||||
{
|
||||
if (ip6 == NULL || mac == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* fe80::/64 + EUI-64 of the MAC (802 group bit complemented), laid out in
|
||||
* network byte order straight into esp_ip6_addr_t. Zone stays 0. */
|
||||
const uint8_t linklocal[16] = {
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
(uint8_t)(mac[0] ^ 0x02), mac[1], mac[2], 0xff,
|
||||
0xfe, mac[3], mac[4], mac[5],
|
||||
};
|
||||
memset(ip6, 0, sizeof(*ip6));
|
||||
memcpy(ip6->addr, linklocal, sizeof(linklocal));
|
||||
}
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add)
|
||||
{
|
||||
if (mac == NULL || wifi_if >= MAX_WIFI_IFS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* The netif handle is owned by this layer (recorded when the interface's RX
|
||||
* callback is registered), so callers only need to supply the interface and
|
||||
* the peer MAC. */
|
||||
esp_netif_t *esp_netif = s_wifi_netifs[wifi_if];
|
||||
if (esp_netif == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
/* Derive the peer's link-local address; esp_netif copies only the address
|
||||
* words for static neighbor entries, so no zone handling is needed here. */
|
||||
esp_ip6_addr_t addr6;
|
||||
esp_wifi_netif_get_ip6_linklocal_from_mac(&addr6, mac);
|
||||
|
||||
return add ? esp_netif_add_static_neighbor(esp_netif, &addr6, mac)
|
||||
: esp_netif_remove_static_neighbor(esp_netif, &addr6);
|
||||
}
|
||||
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||
|
||||
@@ -53,6 +53,8 @@ void esp_nan_action_stop(void);
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
|
||||
#include "esp_private/wifi_types.h"
|
||||
|
||||
#define WIFI_NAN_PAIRING_REASON_NIK_FUP_TIMEOUT 1 /**< Local reason: peer NIK follow-up not received within timeout.
|
||||
See Wi-Fi Aware v4.0 §7.6.4.2 for the NIK-exchange procedure. */
|
||||
|
||||
@@ -76,9 +78,46 @@ typedef struct {
|
||||
uint8_t peer_svc_id;
|
||||
uint8_t peer_nmi[6];
|
||||
enum nan_pairing_role self_role;
|
||||
uint8_t pairing_verification: 1; /**< 1 - PASN verify (re-pair), 0 - PASN auth (bootstrap pairing) */
|
||||
uint8_t reserved: 7;
|
||||
union pairing_cred_t cred;
|
||||
} wifi_nan_pairing_config_t;
|
||||
|
||||
/**
|
||||
* @brief NAN Pairing Bootstrap frame event (request or response).
|
||||
*
|
||||
* @p type is WIFI_NAN_NPBA_TYPE_REQUEST (1) for a bootstrapping request from a peer,
|
||||
* or WIFI_NAN_NPBA_TYPE_RESPONSE (2) for a bootstrapping response.
|
||||
* For requests, @p methods is the selected bootstrapping method.
|
||||
* For responses, @p methods is the matched method, @p status and @p reason_code are valid.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t type; /**< WIFI_NAN_NPBA_TYPE_REQUEST or WIFI_NAN_NPBA_TYPE_RESPONSE */
|
||||
uint8_t peer_svc_id; /**< Peer's service instance id */
|
||||
uint8_t own_svc_id; /**< Own service instance id */
|
||||
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
|
||||
uint16_t methods; /**< selected_method (request) or matched_method (response) */
|
||||
uint8_t status; /**< wifi_nan_pairing_status_t; valid for response */
|
||||
uint8_t reason_code; /**< Rejection reason; valid for response when rejected */
|
||||
} wifi_nan_bootstrap_event_t;
|
||||
|
||||
/**
|
||||
* @brief Callback invoked when a NAN Pairing Bootstrap request or response is received.
|
||||
*
|
||||
* @param evt Bootstrap frame event data.
|
||||
*/
|
||||
typedef void (*esp_nan_app_bootstrap_cb_t)(const wifi_nan_bootstrap_event_t *evt);
|
||||
|
||||
/**
|
||||
* @brief Set the callback for NAN bootstrap request/response frames.
|
||||
*
|
||||
* @param cb Bootstrap callback, or NULL to clear.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: succeed
|
||||
*/
|
||||
esp_err_t esp_nan_app_set_bootstrap_cb(esp_nan_app_bootstrap_cb_t cb);
|
||||
|
||||
/**
|
||||
* @brief NAN Pairing Bootstrapping status values
|
||||
*/
|
||||
@@ -141,7 +180,7 @@ esp_err_t esp_wifi_nan_bootstrap_request(wifi_nan_pairing_bootstrap_req_t *req);
|
||||
* @brief Respond to a NAN Pairing Bootstrapping request from a peer
|
||||
*
|
||||
* @attention This API should be called by the Publisher after receiving a
|
||||
* WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event.
|
||||
* bootstrap indication via the registered NAN bootstrap callback.
|
||||
*
|
||||
* @param resp Pairing bootstrapping response parameters.
|
||||
*
|
||||
@@ -185,6 +224,24 @@ struct nan_pasn_data *esp_nan_app_get_pasn_data(void);
|
||||
*/
|
||||
void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd);
|
||||
|
||||
/**
|
||||
* @brief Clear NM-TK and ND-TK in firmware and host state.
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 octets), or NULL to clear all peers on
|
||||
* @p service_id.
|
||||
* @param service_id Own service id; used only when @p peer_nmi is NULL.
|
||||
*/
|
||||
void esp_nan_app_clear_peer_tks(const uint8_t *peer_nmi, uint8_t service_id);
|
||||
|
||||
/**
|
||||
* @brief Terminate all active NDPs with a peer (PASN verify prep).
|
||||
*
|
||||
* @param publish_id Own publish id
|
||||
*
|
||||
* @return ESP_OK if all ends succeeded or none were active.
|
||||
*/
|
||||
esp_err_t esp_nan_app_end_peer_datapaths(uint8_t publish_id);
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -24,6 +24,7 @@ extern "C" {
|
||||
.disable_random_mac = false, \
|
||||
.reset_current_nvs_creds = false, \
|
||||
.use_nvs_for_caching = false, \
|
||||
.group_mgmt_prot = false, \
|
||||
};
|
||||
|
||||
#define NDP_STATUS_ACCEPTED 1
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_private/wifi.h"
|
||||
#include "esp_wifi_netif.h"
|
||||
@@ -43,6 +44,18 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
|
||||
(void)nira_attr_len;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
|
||||
uint16_t nira_attr_len, uint8_t *own_inst_id)
|
||||
{
|
||||
(void)peer_mac;
|
||||
(void)nira_attr;
|
||||
(void)nira_attr_len;
|
||||
if (own_inst_id) {
|
||||
*own_inst_id = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
|
||||
@@ -220,6 +233,138 @@ void nan_app_clear_paired_peers(void)
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
||||
|
||||
static void nan_app_clear_one_peer_tks(const uint8_t *peer_nmi)
|
||||
{
|
||||
uint8_t key_rsc[8] = {0};
|
||||
static const uint8_t zero_mac[6] = {0};
|
||||
|
||||
if (!peer_nmi || memcmp(peer_nmi, zero_mac, 6) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
|
||||
/* NM-TK is bound to peer NMI (see nan_pasn_install_nan_pairwise_tk). */
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
(uint8_t *)peer_nmi, 1, 1,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_NM_TK);
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
struct ndl_info *ndl = nan_find_ndl(0, (uint8_t *)peer_nmi);
|
||||
uint8_t *key_addr = (uint8_t *)peer_nmi;
|
||||
|
||||
if (ndl) {
|
||||
if (memcmp(ndl->peer_ndi, zero_mac, 6) != 0) {
|
||||
key_addr = ndl->peer_ndi;
|
||||
}
|
||||
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
key_addr, 0, 1,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_ND_TK);
|
||||
|
||||
/* Drop the peer's RX GTK (bound to the peer NDI) and wipe local GTK
|
||||
* state. The TX GTK keyed on the local NDI is released by the blob
|
||||
* when the NDI/interface is torn down. */
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
key_addr, ndl->gtk_keyid, 0,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_ND_GTK);
|
||||
|
||||
/* Drop the peer's RX IGTK/BIGTK (BIP-CMAC-128, installed against the
|
||||
* peer NMI at NDP confirm) so no stale BIP keys linger in the blob. */
|
||||
if (ndl->igtk_set) {
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||
(uint8_t *)peer_nmi, ndl->igtk_keyid, 0,
|
||||
key_rsc, 6,
|
||||
NULL, 0, NAN_KEY_ND_IGTK);
|
||||
}
|
||||
if (ndl->bigtk_set) {
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||
(uint8_t *)peer_nmi, ndl->bigtk_keyid, 0,
|
||||
key_rsc, 6,
|
||||
NULL, 0, NAN_KEY_ND_BIGTK);
|
||||
}
|
||||
|
||||
forced_memzero(ndl->nd_tk, sizeof(ndl->nd_tk));
|
||||
forced_memzero(ndl->nd_kck, sizeof(ndl->nd_kck));
|
||||
forced_memzero(ndl->nd_kek, sizeof(ndl->nd_kek));
|
||||
forced_memzero(ndl->gtk, sizeof(ndl->gtk));
|
||||
forced_memzero(ndl->own_gtk, sizeof(ndl->own_gtk));
|
||||
forced_memzero(ndl->igtk, sizeof(ndl->igtk));
|
||||
forced_memzero(ndl->bigtk, sizeof(ndl->bigtk));
|
||||
ndl->ptk_set = 0;
|
||||
ndl->tk_len = 0;
|
||||
ndl->kck_len = 0;
|
||||
ndl->kek_len = 0;
|
||||
ndl->gtk_set = 0;
|
||||
ndl->own_gtk_set = 0;
|
||||
ndl->gtk_len = 0;
|
||||
ndl->own_gtk_len = 0;
|
||||
ndl->igtk_set = 0;
|
||||
ndl->bigtk_set = 0;
|
||||
ndl->igtk_len = 0;
|
||||
ndl->bigtk_len = 0;
|
||||
}
|
||||
|
||||
/* Fallback when no NDL slot tracks peer_ndi yet. */
|
||||
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
(uint8_t *)peer_nmi, 0, 1,
|
||||
key_rsc, sizeof(key_rsc),
|
||||
NULL, 0, NAN_KEY_ND_TK);
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
||||
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
|
||||
void esp_nan_app_clear_peer_tks(const uint8_t *peer_nmi, uint8_t service_id)
|
||||
{
|
||||
if (peer_nmi) {
|
||||
nan_app_clear_one_peer_tks(peer_nmi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (service_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
uint8_t peer_nmis[NAN_MAX_PEERS_RECORD][MACADDR_LEN];
|
||||
int peer_count = 0;
|
||||
struct own_svc_info *p_own_svc;
|
||||
struct peer_svc_info *temp;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
p_own_svc = nan_find_own_svc(service_id);
|
||||
if (!p_own_svc) {
|
||||
NAN_DATA_UNLOCK();
|
||||
return;
|
||||
}
|
||||
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
|
||||
bool dup = false;
|
||||
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
if (MACADDR_EQUAL(peer_nmis[i], temp->peer_nmi)) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dup && peer_count < NAN_MAX_PEERS_RECORD) {
|
||||
MACADDR_COPY(peer_nmis[peer_count], temp->peer_nmi);
|
||||
peer_count++;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
nan_app_clear_one_peer_tks(peer_nmis[i]);
|
||||
}
|
||||
#else
|
||||
(void)service_id;
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr)
|
||||
@@ -227,20 +372,12 @@ void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr
|
||||
if (ip6 == NULL || mac_addr == NULL) {
|
||||
return;
|
||||
}
|
||||
/* Link-local prefix. */
|
||||
ip6->addr[0] = htonl(0xfe800000ul);
|
||||
ip6->addr[1] = 0;
|
||||
|
||||
/* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
|
||||
ip6->addr[2] = htonl((((uint32_t)(mac_addr[0] ^ 0x02)) << 24) |
|
||||
((uint32_t)(mac_addr[1]) << 16) |
|
||||
((uint32_t)(mac_addr[2]) << 8) |
|
||||
(0xff));
|
||||
ip6->addr[3] = htonl((uint32_t)(0xfeul << 24) |
|
||||
((uint32_t)(mac_addr[3]) << 16) |
|
||||
((uint32_t)(mac_addr[4]) << 8) |
|
||||
(mac_addr[5]));
|
||||
|
||||
/* Reuse the interface-agnostic derivation in the esp_wifi netif layer, then
|
||||
* copy the address words into the lwIP ip6_addr_t. The two structures share
|
||||
* the same layout, which is how esp_netif converts between them. */
|
||||
esp_ip6_addr_t esp_ip6;
|
||||
esp_wifi_netif_get_ip6_linklocal_from_mac(&esp_ip6, mac_addr);
|
||||
memcpy(ip6->addr, esp_ip6.addr, sizeof(ip6->addr));
|
||||
ip6->zone = IP6_NO_ZONE;
|
||||
}
|
||||
|
||||
@@ -491,14 +628,29 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_
|
||||
forced_memzero(&p_svc->derived_security, sizeof(p_svc->derived_security));
|
||||
if (security_cfg) {
|
||||
memcpy(&p_svc->user_cfg, security_cfg, sizeof(*security_cfg));
|
||||
/* Device-global group_mgmt_prot (IGTKSA/BIGTKSA) forces GTKSA on every
|
||||
* secured service: the CSIA capability field has no "IGTK/BIGTK without
|
||||
* GTKSA" encoding (§9.5.21.2 Table 122), so advertising group-management
|
||||
* protection mandates advertising GTKSA. Warn and force it on if the app
|
||||
* requested group_data_prot=0. Forcing support never blocks a peer that
|
||||
* lacks protection — keys activate only after capability negotiation. */
|
||||
if (s_nan_ctx.group_mgmt_prot && !p_svc->user_cfg.group_data_prot) {
|
||||
ESP_LOGW(TAG, "group_data_prot forced ON for '%s': group_mgmt_prot is "
|
||||
"enabled device-wide; GTKSA cannot be advertised without it",
|
||||
svc_name);
|
||||
p_svc->user_cfg.group_data_prot = 1;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
if (pairing) {
|
||||
memcpy(&p_svc->pairing, pairing, sizeof(*pairing));
|
||||
}
|
||||
#else
|
||||
(void)pairing;
|
||||
#endif
|
||||
#else
|
||||
(void)security_cfg;
|
||||
(void)pairing;
|
||||
#endif
|
||||
return p_svc;
|
||||
}
|
||||
@@ -568,6 +720,7 @@ static void nan_record_new_ndl(uint8_t ndp_id, uint8_t publish_id, uint8_t peer_
|
||||
if (ndl && reuse_slot) {
|
||||
ndl->ndp_id = ndp_id;
|
||||
ndl->own_role = own_role;
|
||||
ndl->device_caps = device_caps;
|
||||
return;
|
||||
}
|
||||
if (ndl) {
|
||||
@@ -843,6 +996,9 @@ static void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *pe
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
evt->bootstrapping_methods = nan_app_parse_npba_from_publish(npba);
|
||||
if (peer_info->nira_verified) {
|
||||
evt->already_paired = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
evt->ssi_version = ssi_ver;
|
||||
@@ -869,13 +1025,31 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
|
||||
}
|
||||
uint8_t sub_id = peer_info->peer_svc_id;
|
||||
uint8_t *sub_nmi = peer_info->peer_mac;
|
||||
|
||||
uint8_t *ssi = peer_info->ssi;
|
||||
uint16_t ssi_len = peer_info->ssi_len;
|
||||
uint32_t device_caps = peer_info->device_caps;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
if (!nan_find_peer_svc(pub_id, sub_id, sub_nmi)) {
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(pub_id, sub_id, sub_nmi);
|
||||
if (!p_peer_svc) {
|
||||
p_peer_svc = nan_find_peer_svc(pub_id, 0, sub_nmi);
|
||||
}
|
||||
if (!p_peer_svc) {
|
||||
nan_record_peer_svc(pub_id, sub_id, sub_nmi, device_caps);
|
||||
} else {
|
||||
if (p_peer_svc->svc_id != sub_id) {
|
||||
p_peer_svc->svc_id = sub_id;
|
||||
}
|
||||
if (p_peer_svc->own_svc_id != pub_id) {
|
||||
p_peer_svc->own_svc_id = pub_id;
|
||||
}
|
||||
if (p_peer_svc->device_caps != device_caps) {
|
||||
p_peer_svc->device_caps = device_caps;
|
||||
}
|
||||
if (!MACADDR_EQUAL(p_peer_svc->peer_nmi, sub_nmi)) {
|
||||
MACADDR_COPY(p_peer_svc->peer_nmi, sub_nmi);
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
@@ -890,7 +1064,6 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
|
||||
evt->subscribe_id = sub_id;
|
||||
MACADDR_COPY(evt->sub_if_mac, sub_nmi);
|
||||
|
||||
ESP_LOGI(TAG, "Sent Publish to Peer "MACSTR" [Peer Subscribe id - %d]", MAC2STR(sub_nmi), sub_id);
|
||||
if (ssi && ssi_len) {
|
||||
memcpy(evt->ssi, ssi, ssi_len);
|
||||
evt->ssi_len = ssi_len;
|
||||
@@ -902,8 +1075,8 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
|
||||
}
|
||||
|
||||
static 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)
|
||||
uint8_t *shared_key_attr, uint16_t shared_key_attr_buf_len,
|
||||
struct nan_cb_npba_t *npba)
|
||||
{
|
||||
if (!peer_info) {
|
||||
return;
|
||||
@@ -915,8 +1088,25 @@ static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_inf
|
||||
uint32_t device_caps = peer_info->device_caps;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
if (!nan_find_peer_svc(svc_id, peer_svc_id, peer_mac)) {
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(svc_id, peer_svc_id, peer_mac);
|
||||
if (!p_peer_svc) {
|
||||
p_peer_svc = nan_find_peer_svc(svc_id, 0, peer_mac);
|
||||
}
|
||||
if (!p_peer_svc) {
|
||||
nan_record_peer_svc(svc_id, peer_svc_id, peer_mac, device_caps);
|
||||
} else {
|
||||
if (p_peer_svc->svc_id != peer_svc_id) {
|
||||
p_peer_svc->svc_id = peer_svc_id;
|
||||
}
|
||||
if (p_peer_svc->own_svc_id != svc_id) {
|
||||
p_peer_svc->own_svc_id = svc_id;
|
||||
}
|
||||
if (p_peer_svc->device_caps != device_caps) {
|
||||
p_peer_svc->device_caps = device_caps;
|
||||
}
|
||||
if (!MACADDR_EQUAL(p_peer_svc->peer_nmi, peer_mac)) {
|
||||
MACADDR_COPY(p_peer_svc->peer_nmi, peer_mac);
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
@@ -931,6 +1121,7 @@ static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_inf
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
|
||||
if (npba) {
|
||||
nan_app_parse_npba_from_receive(svc_id, peer_svc_id, peer_mac, npba);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -995,12 +1186,15 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
|
||||
|
||||
nan_record_new_ndl(ndp_id, pub_id, peer_nmi, ESP_WIFI_NDP_ROLE_RESPONDER, device_caps);
|
||||
|
||||
if (!nan_find_peer_svc(pub_id, 0, peer_nmi)) {
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(pub_id, 0, peer_nmi);
|
||||
if (!p_peer_svc) {
|
||||
nan_record_peer_svc(pub_id, 0, peer_nmi, device_caps);
|
||||
} else {
|
||||
p_peer_svc->device_caps = device_caps;
|
||||
}
|
||||
|
||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||
if (ndl && peer_ndi) {
|
||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, peer_nmi);
|
||||
if (ndl) {
|
||||
MACADDR_COPY(ndl->peer_ndi, peer_ndi);
|
||||
}
|
||||
|
||||
@@ -1024,8 +1218,13 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
|
||||
uint8_t own_bssid[6];
|
||||
esp_err_t err = esp_wifi_get_mac(WIFI_IF_NAN, own_bssid);
|
||||
if (err != ESP_OK) {
|
||||
/* Cannot build the auto-response: free the NDL slot and deny the
|
||||
* peer so it does not wait indefinitely. Send outside the lock. */
|
||||
ESP_LOGE(TAG, "get own NAN MAC failed, rc=0x%x; denying NDP ndp_id=%d", err, ndp_id);
|
||||
nan_reset_ndl(ndp_id, false);
|
||||
NAN_DATA_UNLOCK();
|
||||
ESP_LOGE(TAG, "Cannot get own BSSID!");
|
||||
ndp_resp.accept = false;
|
||||
esp_nan_internal_datapath_resp(&ndp_resp, (uint8_t *)&own_ipv6.u_addr.ip6.addr[2]);
|
||||
return;
|
||||
}
|
||||
esp_wifi_nan_get_ipv6_linklocal_from_mac(&own_ipv6.u_addr.ip6, own_bssid);
|
||||
@@ -1207,10 +1406,6 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifndef NAN_KEY_ND_TK
|
||||
#define NAN_KEY_ND_TK 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
if (ndl->security_ctx.type == WIFI_NAN_SECURITY_ENCRYPTED) {
|
||||
uint8_t key_rsc[8] = {0};
|
||||
@@ -1223,12 +1418,103 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
||||
ndl->nd_tk,
|
||||
NAN_NCS_SK_128_TK_LEN,
|
||||
NAN_KEY_ND_TK);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ND-TK", ndl->nd_tk, NAN_NCS_SK_128_TK_LEN, ESP_LOG_DEBUG);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "NDP confirm: failed to install NAN pairwise key (ndp_id=%d, ret=%d)", ndp_id, ret);
|
||||
ESP_LOGE(TAG, "NDP confirm: failed to install ND-TK (ndp_id=%d, ret=%d)", ndp_id, ret);
|
||||
os_free(evt);
|
||||
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
|
||||
goto done;
|
||||
}
|
||||
ret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
peer_nmi,
|
||||
0,
|
||||
1,
|
||||
key_rsc,
|
||||
sizeof(key_rsc),
|
||||
ndl->nd_tk,
|
||||
NAN_NCS_SK_128_TK_LEN,
|
||||
NAN_KEY_NM_TK);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "NDP confirm: failed to install NM-TK (ndp_id=%d, ret=%d)", ndp_id, ret);
|
||||
os_free(evt);
|
||||
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Group keys (ND-GTK) exchanged during NDP setup (§7.1.3.2): our GTK
|
||||
* is the TX key bound to the local NDI; the peer's GTK is the RX key
|
||||
* bound to the peer NDI. Best-effort — a GTK install failure must not
|
||||
* tear down the working unicast datapath. */
|
||||
if (ndl->own_gtk_set && ndl->own_gtk_len) {
|
||||
int gret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
own_ndi, ndl->own_gtk_keyid, 1,
|
||||
ndl->own_gtk_rsc, NAN_KEY_RSC_LEN,
|
||||
ndl->own_gtk, ndl->own_gtk_len,
|
||||
NAN_KEY_ND_GTK);
|
||||
if (gret != 0) {
|
||||
ESP_LOGW(TAG, "NDP confirm: own GTK (TX) install failed (ndp_id=%d, ret=%d)", ndp_id, gret);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "NDP confirm: own GTK (TX) installed (keyid=%d)", ndl->own_gtk_keyid);
|
||||
}
|
||||
ESP_LOG_BUFFER_HEXDUMP("ND-GTK", ndl->own_gtk, ndl->own_gtk_len, ESP_LOG_DEBUG);
|
||||
}
|
||||
if (ndl->gtk_set && ndl->gtk_len) {
|
||||
int gret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||
peer_ndi, ndl->gtk_keyid, 0,
|
||||
ndl->gtk_rsc, NAN_KEY_RSC_LEN,
|
||||
ndl->gtk, ndl->gtk_len,
|
||||
NAN_KEY_ND_GTK);
|
||||
if (gret != 0) {
|
||||
ESP_LOGW(TAG, "NDP confirm: peer GTK (RX) install failed (ndp_id=%d, ret=%d)", ndp_id, gret);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "NDP confirm: peer GTK (RX) installed (keyid=%d)", ndl->gtk_keyid);
|
||||
}
|
||||
}
|
||||
|
||||
/* Own IGTK/BIGTK (TX) are installed once at NAN start (see
|
||||
* nan_security_install_own_group_integrity_keys); not re-installed here,
|
||||
* to preserve the blob's monotonic BIPN/IPN across the session. Only the
|
||||
* peer RX keys are bound at NDP confirm. §7.1.3.3/§7.1.3.4; NMI==NDI today.
|
||||
* Peer IGTK/BIGTK install RX-only against the peer NMI, seeding the BIP
|
||||
* RX replay counter with the peer's advertised IPN/BIPN from the KDE. */
|
||||
if (ndl->igtk_set && ndl->igtk_len) {
|
||||
if (ndl->igtk_len != NAN_ND_GTK_LEN) {
|
||||
ESP_LOGW(TAG, "NDP confirm: peer IGTK len=%d unsupported (BIP-CMAC-128 only); skipping",
|
||||
ndl->igtk_len);
|
||||
} else {
|
||||
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||
peer_nmi, ndl->igtk_keyid, 0,
|
||||
ndl->igtk_ipn, 6,
|
||||
ndl->igtk, ndl->igtk_len,
|
||||
NAN_KEY_ND_IGTK);
|
||||
if (r != 0) {
|
||||
ESP_LOGW(TAG, "NDP confirm: peer IGTK (RX) install failed, rc=0x%x (ndp_id=%d)", r, ndp_id);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "NDP confirm: peer IGTK (RX) installed (keyid=%d)", ndl->igtk_keyid);
|
||||
}
|
||||
/* Peer IGTK bytes for sniffer MIC cross-check vs the peer's multicast SDFs. */
|
||||
ESP_LOG_BUFFER_HEXDUMP("PEER ND-IGTK", ndl->igtk, ndl->igtk_len, ESP_LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
if (ndl->bigtk_set && ndl->bigtk_len) {
|
||||
if (ndl->bigtk_len != NAN_ND_GTK_LEN) {
|
||||
ESP_LOGW(TAG, "NDP confirm: peer BIGTK len=%d unsupported (BIP-CMAC-128 only); skipping",
|
||||
ndl->bigtk_len);
|
||||
} else {
|
||||
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||
peer_nmi, ndl->bigtk_keyid, 0,
|
||||
ndl->bigtk_ipn, 6,
|
||||
ndl->bigtk, ndl->bigtk_len,
|
||||
NAN_KEY_ND_BIGTK);
|
||||
if (r != 0) {
|
||||
ESP_LOGW(TAG, "NDP confirm: peer BIGTK (RX) install failed, rc=0x%x (ndp_id=%d)", r, ndp_id);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "NDP confirm: peer BIGTK (RX) installed (keyid=%d)", ndl->bigtk_keyid);
|
||||
}
|
||||
/* Peer BIGTK bytes for sniffer MIC cross-check vs the peer's protected Beacons. */
|
||||
ESP_LOG_BUFFER_HEXDUMP("PEER ND-BIGTK", ndl->bigtk, ndl->bigtk_len, ESP_LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
||||
evt->status = status;
|
||||
@@ -1250,8 +1536,6 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, ssi, ssi_len, ESP_LOG_DEBUG);
|
||||
}
|
||||
|
||||
esp_netif_action_connected(s_nan_ctx.nan_netif, WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, evt);
|
||||
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
ip6_addr_t peer_ip6 = {0};
|
||||
@@ -1262,6 +1546,20 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
||||
ESP_LOGI(TAG, "NDP confirmed with Peer "MACSTR" [NDP ID - %d, Peer IPv6 - %s]",
|
||||
MAC2STR(peer_nmi), ndp_id, inet6_ntoa(peer_ip6));
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
/* Pin the peer's link-local -> NDI mapping so traffic to the peer skips
|
||||
* Neighbor Discovery (no NS/NA) on the NAN link. The esp_wifi netif layer
|
||||
* owns the netif lookup and derives the peer's link-local from its NDI
|
||||
* (the address the peer actually sources from). */
|
||||
esp_err_t nbr_err = esp_wifi_netif_set_static_neighbor(WIFI_IF_NAN, peer_ndi, true);
|
||||
if (nbr_err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "static nbr ADD failed: %s", esp_err_to_name(nbr_err));
|
||||
}
|
||||
#else
|
||||
esp_netif_action_connected(s_nan_ctx.nan_netif, WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, evt);
|
||||
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
|
||||
#endif
|
||||
|
||||
os_event_group_set_bits(nan_event_group, NDP_ACCEPTED);
|
||||
nan_app_post_event(WIFI_EVENT_NDP_CONFIRM, evt, evt_data_len);
|
||||
os_free(evt);
|
||||
@@ -1284,6 +1582,15 @@ static void nan_app_ndp_terminated_cb(uint8_t reason, uint8_t ndp_id, uint8_t in
|
||||
s_nan_ctx.event &= ~(NDP_INDICATION);
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
/* Drop the peer's static neighbor mapping added on NDP confirm. (It is also
|
||||
* cleared automatically if the NAN netif goes down on the last datapath.) */
|
||||
esp_err_t nbr_err = esp_wifi_netif_set_static_neighbor(WIFI_IF_NAN, init_ndi, false);
|
||||
if (nbr_err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "static nbr DEL failed: %s", esp_err_to_name(nbr_err));
|
||||
}
|
||||
#endif
|
||||
|
||||
wifi_event_ndp_terminated_t *evt = (wifi_event_ndp_terminated_t *)os_zalloc(sizeof(wifi_event_ndp_terminated_t));
|
||||
if (!evt) {
|
||||
ESP_LOGE(TAG, "Failed to allocate for event");
|
||||
@@ -1311,6 +1618,7 @@ static void nan_action_txdone_cb(uint32_t context, bool tx_status)
|
||||
|
||||
static void esp_nan_ndp_tx_done_cb(uint8_t ndp_id, const uint8_t *peer_nmi, uint8_t msg_type, bool tx_status)
|
||||
{
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
|
||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||
@@ -1353,6 +1661,7 @@ static struct nan_secure_dp_funcs s_nan_secure_dp_funcs = {
|
||||
.ndp_security_install_get_shared_desc_len = esp_nan_ndp_security_install_get_shared_desc_len,
|
||||
.get_ndp_resp_num_pmkids = esp_nan_get_ndp_resp_num_pmkids,
|
||||
.get_ndp_resp_shared_key_desc_len = esp_nan_get_ndp_resp_shared_key_desc_len,
|
||||
.ndp_confirm_get_shared_desc_len = esp_nan_ndp_confirm_get_shared_desc_len,
|
||||
|
||||
/* CSIA / SCIA construction */
|
||||
.construct_csia = esp_nan_construct_csia,
|
||||
@@ -1440,6 +1749,71 @@ void esp_nan_app_init(void)
|
||||
esp_nan_internal_register_secure_dp_funcs(&s_nan_secure_dp_funcs);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
|
||||
static bool nan_peer_cred_npk_present(const wifi_nan_peer_creds_t *c)
|
||||
{
|
||||
static const uint8_t zero_npk[ESP_WIFI_NAN_NPK_LEN] = {0};
|
||||
|
||||
if (!c || !c->is_valid) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(c->npk, zero_npk, ESP_WIFI_NAN_NPK_LEN) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool nan_peer_nik_cached_cb(uint8_t *peer_mac)
|
||||
{
|
||||
bool cached = false;
|
||||
struct peer_svc_info *peer;
|
||||
|
||||
if (!peer_mac) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
peer = nan_find_peer_svc(0, 0, peer_mac);
|
||||
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
|
||||
if (peer) {
|
||||
struct own_svc_info *own = nan_find_own_svc(peer->own_svc_id);
|
||||
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (!s_nan_ctx.peer_creds[i].is_valid) {
|
||||
continue;
|
||||
}
|
||||
if (!nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
|
||||
continue;
|
||||
}
|
||||
if (own && memcmp(s_nan_ctx.peer_creds[i].service_hash, own->svc_hash, 6) == 0) {
|
||||
cached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cached) {
|
||||
uint8_t npk_slots = 0;
|
||||
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (s_nan_ctx.peer_creds[i].is_valid &&
|
||||
nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
|
||||
npk_slots++;
|
||||
}
|
||||
}
|
||||
if (npk_slots == 1) {
|
||||
cached = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (peer && peer->has_nik) {
|
||||
cached = true;
|
||||
}
|
||||
#endif
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return cached;
|
||||
}
|
||||
#endif
|
||||
|
||||
void esp_nan_action_start(esp_netif_t *nan_netif)
|
||||
{
|
||||
nan_set_app_default_handlers();
|
||||
@@ -1462,12 +1836,24 @@ void esp_nan_action_start(esp_netif_t *nan_netif)
|
||||
.get_nira_len = esp_nan_get_nira_len,
|
||||
.construct_nira = esp_nan_construct_nira,
|
||||
.verify_nira = esp_nan_verify_nira,
|
||||
.peer_nik_cached = nan_peer_nik_cached_cb,
|
||||
.receive_pasn = handle_auth_pasn,
|
||||
#endif
|
||||
};
|
||||
esp_nan_internal_register_callbacks(&nan_cb);
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
/* s_nan_ctx.group_mgmt_prot (device-global IGTKSA/BIGTKSA, one per NMI) was
|
||||
* captured from the user's start config in esp_wifi_nan_sync_start().
|
||||
* Install the device-global IGTK/BIGTK for TX now (when enabled) so Beacons
|
||||
* (BIGTK) and group-addressed SDFs (IGTK) are BIP-protected from the first
|
||||
* frame, like iOS. The blob gates beacon BIP-TX on an active BIGTK index
|
||||
* only (no NDP state), so installing here is sufficient. */
|
||||
nan_security_install_own_group_integrity_keys();
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "NAN Discovery started.");
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||
os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT);
|
||||
}
|
||||
|
||||
@@ -1493,7 +1879,15 @@ void esp_nan_action_stop(void)
|
||||
nan_app_clear_paired_peers();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
/* Drop the device-global IGTK/BIGTK so the next start regenerates fresh
|
||||
* keys instead of re-installing a stale key with IPN/BIPN=0 (which would
|
||||
* reset the blob's replay counter) — see nan_security_reset_own_group_keys. */
|
||||
nan_security_reset_own_group_keys();
|
||||
#endif
|
||||
|
||||
esp_nan_internal_register_callbacks(NULL);
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
|
||||
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||
}
|
||||
|
||||
@@ -1516,7 +1910,6 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* XXX: For now, NAN-USD and NAN-Sync can not coexist. */
|
||||
/* NAN-Synchronization Only */
|
||||
wifi_config_t config = {0};
|
||||
@@ -1536,10 +1929,17 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
s_nan_ctx.num_peer_creds = 0;
|
||||
memset(s_nan_ctx.peer_creds, 0, sizeof(s_nan_ctx.peer_creds));
|
||||
s_nan_ctx.use_nvs_for_caching = nan_cfg->use_nvs_for_caching;
|
||||
s_nan_ctx.group_mgmt_prot = nan_cfg->group_mgmt_prot;
|
||||
s_nan_ctx.nik_lifetime = 0;
|
||||
|
||||
if (nan_cfg->reset_current_nvs_creds) {
|
||||
/* Start from a clean slate: drop every credential persisted in NVS. */
|
||||
esp_wifi_nan_erase_all_creds();
|
||||
ret = esp_wifi_nan_erase_all_creds();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase NAN credentials from NVS");
|
||||
NAN_DATA_UNLOCK();
|
||||
return ret;
|
||||
}
|
||||
} else if (esp_wifi_nan_load_saved_creds(s_nan_ctx.own_nik, &s_nan_ctx.own_nik_valid,
|
||||
s_nan_ctx.peer_creds, &s_nan_ctx.num_peer_creds) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Failed to load saved NAN credentials");
|
||||
@@ -1557,7 +1957,12 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
/* Persist the freshly generated NIK only when NVS caching is enabled;
|
||||
* otherwise the identity stays ephemeral for this session. */
|
||||
if (s_nan_ctx.use_nvs_for_caching) {
|
||||
esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
|
||||
ret = esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to persist own NIK to NVS");
|
||||
NAN_DATA_UNLOCK();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Drop the cached NIRA tag; it was derived from the previous NIK. */
|
||||
@@ -1570,6 +1975,7 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_sync_config_t));
|
||||
ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_NAN, &config), TAG, "Setting NAN config failed");
|
||||
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
|
||||
if (esp_wifi_start() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Starting wifi failed");
|
||||
NAN_DATA_LOCK();
|
||||
@@ -1616,6 +2022,8 @@ esp_err_t esp_wifi_nan_sync_stop(void)
|
||||
NAN_DATA_UNLOCK();
|
||||
}
|
||||
|
||||
/* Wait for a fresh stop event, not a stale bit from prior run. */
|
||||
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||
ESP_RETURN_ON_ERROR(esp_wifi_stop(), TAG, "Stopping NAN failed");
|
||||
|
||||
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STOPPED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
|
||||
@@ -1650,7 +2058,6 @@ static bool nan_check_paired_service_hash(uint8_t service_hash[6])
|
||||
uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
|
||||
{
|
||||
int pub_id = 0;
|
||||
uint8_t service_id[6] = {0};
|
||||
|
||||
if (publish_cfg->usd_discovery_flag && !s_usd_in_progress) {
|
||||
ESP_LOGE(TAG, "Can not start Publish function with USD Discovery "
|
||||
@@ -1711,6 +2118,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
wifi_nan_publish_cfg_t *cfg = NULL;
|
||||
uint8_t service_id[6] = {0};
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
if (!(s_nan_ctx.state & NAN_STARTED_BIT)) {
|
||||
@@ -1788,7 +2196,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
if (nan_check_paired_service_hash(service_id)) {
|
||||
if (cfg->pairing && nan_check_paired_service_hash(service_id)) {
|
||||
cfg->pairing->pairing_setup = false;
|
||||
}
|
||||
#endif
|
||||
@@ -1840,7 +2248,6 @@ fail:
|
||||
uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg)
|
||||
{
|
||||
int sub_id = 0;
|
||||
uint8_t service_id[6] = {0};
|
||||
|
||||
if (subscribe_cfg->usd_discovery_flag && !s_usd_in_progress) {
|
||||
ESP_LOGE(TAG, "Can not start Subscribe function with USD Discovery "
|
||||
@@ -1895,6 +2302,9 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
wifi_nan_subscribe_cfg_t *cfg = NULL;
|
||||
uint8_t service_id[6] = {0};
|
||||
|
||||
if (subscribe_cfg->security_reqd) {
|
||||
#ifndef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
ESP_LOGE(TAG, "Encrypted datapath not enabled (CONFIG_ESP_WIFI_NAN_SECURITY)");
|
||||
@@ -1939,38 +2349,64 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cfg = os_zalloc(sizeof(*cfg));
|
||||
if (!cfg) {
|
||||
ESP_LOGE(TAG, "Failed to allocate subscribe config");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(cfg, subscribe_cfg, sizeof(*cfg));
|
||||
cfg->pairing = NULL;
|
||||
if (subscribe_cfg->pairing) {
|
||||
cfg->pairing = os_malloc(sizeof(*cfg->pairing));
|
||||
if (!cfg->pairing) {
|
||||
ESP_LOGE(TAG, "Failed to copy pairing config");
|
||||
goto fail;
|
||||
}
|
||||
memcpy(cfg->pairing, subscribe_cfg->pairing, sizeof(*cfg->pairing));
|
||||
}
|
||||
|
||||
/* Pre-claim host slot BEFORE the blob's subscribe call; see comment on
|
||||
* the publish path for the watchdog rationale. */
|
||||
|
||||
if (!nan_compute_service_id(subscribe_cfg->service_name, service_id)) {
|
||||
ESP_LOGE(TAG, "Failed to compute Service ID for %s", subscribe_cfg->service_name);
|
||||
if (!nan_compute_service_id(cfg->service_name, service_id)) {
|
||||
ESP_LOGE(TAG, "Failed to compute Service ID for %s", cfg->service_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
if (nan_check_paired_service_hash(service_id)) {
|
||||
subscribe_cfg->pairing->pairing_setup = false;
|
||||
if (cfg->pairing && nan_check_paired_service_hash(service_id)) {
|
||||
cfg->pairing->pairing_setup = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name,
|
||||
subscribe_cfg->security_cfg, subscribe_cfg->pairing)) {
|
||||
if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, cfg->service_name,
|
||||
cfg->security_cfg, cfg->pairing)) {
|
||||
ESP_LOGE(TAG, "No free service slot");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (esp_nan_internal_subscribe_service(subscribe_cfg, (uint8_t *) &sub_id, false) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", subscribe_cfg->service_name);
|
||||
nan_abort_own_svc(subscribe_cfg->service_name);
|
||||
if (esp_nan_internal_subscribe_service(cfg, (uint8_t *) &sub_id, false) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", cfg->service_name);
|
||||
nan_abort_own_svc(cfg->service_name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", subscribe_cfg->service_name, sub_id);
|
||||
nan_finalize_own_svc(subscribe_cfg->service_name, (uint8_t) sub_id, false, service_id);
|
||||
ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", cfg->service_name, sub_id);
|
||||
nan_finalize_own_svc(cfg->service_name, (uint8_t) sub_id, false, service_id);
|
||||
if (cfg->pairing) {
|
||||
os_free(cfg->pairing);
|
||||
}
|
||||
os_free(cfg);
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
return sub_id;
|
||||
fail:
|
||||
if (cfg) {
|
||||
if (cfg->pairing) {
|
||||
os_free(cfg->pairing);
|
||||
}
|
||||
os_free(cfg);
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
return 0;
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
|
||||
@@ -2071,6 +2507,12 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
/* Snapshot peer NMIs before cancel; clear TKs only after a successful cancel
|
||||
* so a failed attempt does not leave an active service without keys. */
|
||||
uint8_t peer_nmis[NAN_MAX_PEERS_RECORD][MACADDR_LEN];
|
||||
int peer_count = 0;
|
||||
#endif
|
||||
NAN_DATA_LOCK();
|
||||
struct own_svc_info *p_own_svc = nan_find_own_svc(service_id);
|
||||
|
||||
@@ -2079,6 +2521,27 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
{
|
||||
struct peer_svc_info *temp;
|
||||
|
||||
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
|
||||
bool dup = false;
|
||||
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
if (MACADDR_EQUAL(peer_nmis[i], temp->peer_nmi)) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dup && peer_count < NAN_MAX_PEERS_RECORD) {
|
||||
MACADDR_COPY(peer_nmis[peer_count], temp->peer_nmi);
|
||||
peer_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_own_svc->type == ESP_NAN_PUBLISH) {
|
||||
if (esp_nan_internal_publish_service(NULL, &service_id, true) == ESP_OK) {
|
||||
nan_reset_service(service_id, false);
|
||||
@@ -2101,18 +2564,24 @@ fail:
|
||||
|
||||
done:
|
||||
NAN_DATA_UNLOCK();
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
/* Cancel succeeded; now safe to wipe pairwise keys for the collected peers. */
|
||||
for (int i = 0; i < peer_count; i++) {
|
||||
nan_app_clear_one_peer_tks(peer_nmis[i]);
|
||||
}
|
||||
#endif
|
||||
return ESP_OK;
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
|
||||
uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req)
|
||||
{
|
||||
uint8_t ndp_id = 0;
|
||||
uint8_t own_bssid[6];
|
||||
ip_addr_t own_ipv6 = {0};
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(0, req->pub_id, req->peer_mac);
|
||||
|
||||
@@ -2230,7 +2699,6 @@ esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp)
|
||||
ESP_LOGE(TAG, "Need NDP Indication before NDP Response can be sent");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (MACADDR_EQUAL(resp->peer_mac, null_mac)) {
|
||||
MACADDR_COPY(resp->peer_mac, ndl->peer_nmi);
|
||||
}
|
||||
@@ -2302,6 +2770,30 @@ esp_err_t esp_wifi_nan_datapath_end(wifi_nan_datapath_end_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
esp_err_t esp_nan_app_end_peer_datapaths(uint8_t publish_id)
|
||||
{
|
||||
wifi_nan_datapath_end_req_t ndp_end;
|
||||
int i;
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
for (i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
|
||||
struct ndl_info *ndl = &s_nan_ctx.ndl[i];
|
||||
|
||||
if (ndl->publisher_id == publish_id) {
|
||||
ndp_end.ndp_id = ndl->ndp_id;
|
||||
MACADDR_COPY(ndp_end.peer_mac, ndl->peer_nmi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
if (i == ESP_WIFI_NAN_DATAPATH_MAX_PEERS)
|
||||
return ESP_FAIL;
|
||||
|
||||
return esp_wifi_nan_datapath_end(&ndp_end);
|
||||
}
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int *num_peer_records)
|
||||
{
|
||||
struct own_svc_info *own_svc = NULL;
|
||||
|
||||
@@ -130,6 +130,40 @@ extern void *s_nan_data_lock;
|
||||
#define NAN_KEY_INFO_ENC_KEY BIT(12)
|
||||
#define NAN_KEY_INFO_KEY_TYPE BIT(3) /* 1=Pairwise, 0=Group */
|
||||
|
||||
/* NAN KDE OUIs and Data Types carried in the Key Data field (Wi-Fi Aware
|
||||
* v4.0 §9.5.21.5 Table 126; formats per 802.11 Fig 12-36/12-42/12-47). */
|
||||
#define NAN_KDE_OUI_RSN 0x000FACUL
|
||||
#define NAN_KDE_OUI_WFA 0x506F9AUL
|
||||
#define NAN_KDE_TYPE_GTK 1 /* 00-0F-AC GTK KDE */
|
||||
#define NAN_KDE_TYPE_MAC 3 /* 00-0F-AC MAC address KDE */
|
||||
#define NAN_KDE_TYPE_IGTK 9 /* 00-0F-AC IGTK KDE */
|
||||
#define NAN_KDE_TYPE_BIGTK 14 /* 00-0F-AC BIGTK KDE */
|
||||
#define NAN_KDE_TYPE_NIK 36 /* 50-6F-9A NIK KDE */
|
||||
#define NAN_KDE_TYPE_KEY_LIFE 37 /* 50-6F-9A NAN Key Lifetime KDE */
|
||||
|
||||
/* KDE inner-prefix lengths (bytes before the actual key material). */
|
||||
#define NAN_KDE_HDR_LEN 6 /* DD(1) + len(1) + OUI(3) + DataType(1) */
|
||||
#define NAN_GTK_KDE_PREFIX_LEN 2 /* KeyID/Tx(1) + Reserved(1) */
|
||||
#define NAN_IGTK_KDE_PREFIX_LEN 8 /* KeyID(2) + IPN(6) */
|
||||
#define NAN_BIGTK_KDE_PREFIX_LEN 8 /* KeyID(2) + BIPN(6) */
|
||||
|
||||
/* CSIA Capabilities group-SA support (§9.5.21.2 Table 122, bits 1-2, bit 2 high
|
||||
* order). Mirrors hostap nan_defs.h NAN_CS_INFO_CAPA_GTK_SUPP_*. */
|
||||
#define NAN_CSIA_CAP_GTK_SUPP_POS 1
|
||||
#define NAN_CSIA_CAP_GTK_SUPP_MASK 0x06
|
||||
#define NAN_CSIA_CAP_GTK_SUPP_NONE 0 /* 00: no group SA */
|
||||
#define NAN_CSIA_CAP_GTK_SUPP_IGTK 1 /* 01: GTKSA + IGTKSA */
|
||||
#define NAN_CSIA_CAP_GTK_SUPP_ALL 2 /* 10: GTKSA + IGTKSA + BIGTKSA */
|
||||
|
||||
/* ND-GTK is the data-path group key (CCMP-128). */
|
||||
#define NAN_ND_GTK_LEN 16
|
||||
/* Host-side bound for the group Key Data scratch buffers (plaintext + AES-wrap),
|
||||
* sized for GTK+IGTK+BIGTK: GTK 24B + IGTK 30B + BIGTK 30B + optional Key Lifetime
|
||||
* KDE(s), padded to a multiple of 8, + 8B NIST AES Key Wrap overhead (~104B worst
|
||||
* case). NOT the descriptor-len reservation — the getters now return the EXACT
|
||||
* per-NDP length, so the blob allocates exactly what the builder writes. */
|
||||
#define NAN_GROUP_KEY_DATA_MAX 128
|
||||
|
||||
/* NCS-SK-128 only for now (Table 21): KCK 128 bits, KEK 128 bits, TK 128 bits, MIC 16 bytes */
|
||||
#define NAN_NCS_SK_128_KCK_LEN 16
|
||||
#define NAN_NCS_SK_128_KEK_LEN 16
|
||||
@@ -138,11 +172,16 @@ extern void *s_nan_data_lock;
|
||||
#define NAN_NCS_SK_128_PTK_LEN (NAN_NCS_SK_128_KCK_LEN + NAN_NCS_SK_128_KEK_LEN + NAN_NCS_SK_128_TK_LEN)
|
||||
|
||||
/* Internal key-install constants matching esp_wifi_set_sta_key_internal semantics. */
|
||||
#define NAN_WIFI_WPA_ALG_CCMP 3
|
||||
#define NAN_WIFI_WPA_ALG_CCMP 3
|
||||
#define NAN_WIFI_WPA_ALG_BIP_CMAC_128 7 /* IGTK/BIGTK BIP = blob WIFI_WPA_ALG_IGTK; 4 is SMS4 */
|
||||
#define NAN_KEY_FLAG_RX BIT(2)
|
||||
#define NAN_KEY_FLAG_TX BIT(3)
|
||||
#define NAN_KEY_FLAG_PAIRWISE BIT(5)
|
||||
|
||||
/* NAN key-type selector (nan_key_type_t: NAN_KEY_ND_TK / ND_GTK / NM_TK / ND_IGTK /
|
||||
* ND_BIGTK), passed as the last arg of esp_wifi_set_nan_key_internal, is defined in
|
||||
* esp_wifi_driver.h (included above) and shared with the blob. */
|
||||
|
||||
/* Handshake state */
|
||||
enum nan_handshake_state {
|
||||
NAN_HANDSHAKE_IDLE = 0,
|
||||
@@ -195,6 +234,13 @@ struct peer_svc_info {
|
||||
* whose ND-PMKID matched the publisher SCIA. The initiator NDP-req path
|
||||
* uses this to pick the right credential for M1's pair-PMKID. */
|
||||
uint8_t matched_cred_idx;
|
||||
/* Set at SDF match if the publisher advertised an NCS-GTK suite in its CSIA;
|
||||
* the initiator NDP-req path uses it (with our own group_data_prot) to
|
||||
* decide whether to distribute a GTK during NDP setup. */
|
||||
uint8_t peer_group_data_cap: 1;
|
||||
uint8_t peer_group_mgmt_cap: 1; /* peer advertised IGTKSA (CSIA caps bits 1-2 != 0) */
|
||||
uint8_t peer_group_bigtk_cap: 1; /* peer advertised BIGTKSA (CSIA caps bits 1-2 == 10) */
|
||||
uint8_t peer_sec_reserved: 5;
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
/* Peer NIK / cipher version / lifetime extracted from a NAN Shared Key
|
||||
@@ -231,6 +277,11 @@ struct own_svc_info {
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
bool nik_fup_pending;
|
||||
uint8_t nik_fup_pending_peer_nmi[MACADDR_LEN];
|
||||
/* Set when the current PASN session for @c verify_session_peer_nmi is a
|
||||
* pairing verification (re-pair). Consumed once in the key-installed
|
||||
* callback to skip the NIK follow-up exchange. */
|
||||
bool verify_session_pending;
|
||||
uint8_t verify_session_peer_nmi[MACADDR_LEN];
|
||||
#endif
|
||||
uint8_t svc_hash[6];
|
||||
};
|
||||
@@ -268,20 +319,38 @@ struct ndl_info {
|
||||
|
||||
uint8_t handshake_state;
|
||||
|
||||
/* Group key state (unsupported -- pairwise-only M1-M4 flow today;
|
||||
* fields kept to match the spec-defined RSNA key descriptor layout
|
||||
* and stay forward-compatible). */
|
||||
/* Received peer group keys (RX GTKSA). GTK protects group-addressed data
|
||||
* frames the peer transmits; installed against the peer NDI. IGTK/BIGTK
|
||||
* protect group-addressed management/Beacon frames (NMI plane). */
|
||||
uint8_t gtk[NAN_GTK_MAX_LEN];
|
||||
uint8_t igtk[NAN_GTK_MAX_LEN];
|
||||
uint8_t bigtk[NAN_GTK_MAX_LEN];
|
||||
uint8_t gtk_len;
|
||||
uint8_t igtk_len;
|
||||
uint8_t bigtk_len;
|
||||
uint8_t gtk_keyid; /* peer GTK Key ID (1 or 2) */
|
||||
uint8_t igtk_keyid; /* peer IGTK Key ID (4 or 5) */
|
||||
uint8_t bigtk_keyid; /* peer BIGTK Key ID (6 or 7) */
|
||||
uint8_t gtk_rsc[NAN_KEY_RSC_LEN]; /* peer GTK RSC from Key RSC field */
|
||||
uint8_t igtk_ipn[6]; /* peer IGTK IPN (seeds BIP RX replay counter) */
|
||||
uint8_t bigtk_ipn[6]; /* peer BIGTK BIPN (seeds BIP RX replay counter) */
|
||||
uint8_t gtk_set: 1;
|
||||
uint8_t igtk_set: 1;
|
||||
uint8_t bigtk_set: 1;
|
||||
uint8_t group_keys_reserved: 5;
|
||||
|
||||
/* Own group key (TX GTKSA) distributed to the peer in M3 (initiator) or
|
||||
* M4 (responder); installed against the local NDI as the TX group key. */
|
||||
uint8_t own_gtk[NAN_ND_GTK_LEN];
|
||||
uint8_t own_gtk_len;
|
||||
uint8_t own_gtk_keyid; /* own GTK Key ID (1 or 2) */
|
||||
uint8_t own_gtk_rsc[NAN_KEY_RSC_LEN];
|
||||
uint8_t own_gtk_set: 1;
|
||||
uint8_t gtk_required: 1; /* GTKSA negotiated for this NDP */
|
||||
uint8_t igtk_required: 1; /* IGTKSA negotiated for this NDP */
|
||||
uint8_t bigtk_required: 1; /* BIGTKSA negotiated for this NDP */
|
||||
uint8_t own_group_reserved: 4;
|
||||
|
||||
uint8_t key_rsc[NAN_KEY_RSC_LEN];
|
||||
#endif
|
||||
};
|
||||
@@ -299,6 +368,28 @@ typedef struct {
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN];
|
||||
bool own_nik_valid;
|
||||
/* Device-global IGTKSA/BIGTKSA (one per NMI, §7.1.3.3/§7.1.3.4). Generated
|
||||
* once via os_get_random and reused across all secured NDPs; copied into
|
||||
* each M3/M4 and installed against the local NMI. BIP-CMAC-128 (16-byte).
|
||||
* On ESP the NMI and NDI are the same MAC today. */
|
||||
uint8_t own_igtk[NAN_ND_GTK_LEN];
|
||||
uint8_t own_igtk_ipn[6];
|
||||
uint8_t own_igtk_keyid; /* 4 or 5 */
|
||||
bool own_igtk_set;
|
||||
uint8_t own_bigtk[NAN_ND_GTK_LEN];
|
||||
uint8_t own_bigtk_bipn[6];
|
||||
uint8_t own_bigtk_keyid; /* 6 or 7 */
|
||||
bool own_bigtk_set;
|
||||
/* Device-global "support + use group management protection (IGTKSA/BIGTKSA)".
|
||||
* One setting per NMI, not per service: Beacons are NMI-level and there is
|
||||
* exactly one IGTKSA/BIGTKSA per NMI (§7.1.3.3/§7.1.3.4). Sourced from
|
||||
* wifi_nan_sync_config_t.group_mgmt_prot at NAN start. When set it: forces
|
||||
* GTKSA on every secured service (the CSIA caps field cannot encode IGTK/
|
||||
* BIGTK without GTKSA, §9.5.21.2 Table 122), generates own IGTK+BIGTK,
|
||||
* advertises caps 0x04, and BIP-protects Beacons + multicast SDFs.
|
||||
* Advertising never blocks a non-supporting peer — keys and protection are
|
||||
* set up only after capability negotiation (§7.1.3.5). */
|
||||
bool group_mgmt_prot;
|
||||
uint8_t cached_nira_nonce[8];
|
||||
uint8_t cached_nira_tag[8];
|
||||
bool nira_cached;
|
||||
@@ -308,6 +399,7 @@ typedef struct {
|
||||
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS];
|
||||
uint8_t num_peer_creds;
|
||||
bool use_nvs_for_caching;
|
||||
uint32_t nik_lifetime;
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT
|
||||
struct nan_pasn_data *nan_pasn_data;
|
||||
@@ -334,7 +426,8 @@ bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]);
|
||||
uint32_t esp_nan_get_csia_len(uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
|
||||
uint32_t esp_nan_get_scia_len(uint8_t num_pmkids);
|
||||
uint32_t esp_nan_get_shared_key_desc_attr_len(uint16_t key_data_len);
|
||||
int esp_nan_ndp_security_install_get_shared_desc_len(void);
|
||||
int esp_nan_ndp_security_install_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
int esp_nan_ndp_confirm_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
uint8_t esp_nan_get_ndp_resp_num_pmkids(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
uint32_t esp_nan_get_ndp_resp_shared_key_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||
|
||||
@@ -422,6 +515,17 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
|
||||
const struct peer_svc_info *peer_svc,
|
||||
const uint8_t *peer_nmi);
|
||||
|
||||
/* Generate (once) and TX-install the device-global IGTK/BIGTK so Beacons and
|
||||
* group-addressed SDFs are BIP-protected from NAN start (§7.1.3.3/§7.1.3.4).
|
||||
* Call once at NAN start; never per-NDP (would reset the blob's BIPN counter). */
|
||||
void nan_security_install_own_group_integrity_keys(void);
|
||||
|
||||
/* Clear the device-global IGTK/BIGTK state on NAN stop so the next NAN start
|
||||
* regenerates fresh keys. Prevents re-installing a stale key with IPN/BIPN=0
|
||||
* (which resets the blob's monotonic replay counter) and key reuse if the NMI
|
||||
* is re-randomized on restart. */
|
||||
void nan_security_reset_own_group_keys(void);
|
||||
|
||||
/*
|
||||
* Match subscriber discovery security to a publisher's params.
|
||||
* NCS-SK: Compare locally derived ND-PMKID (subscriber passphrase, publisher NMI) to
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -116,6 +116,15 @@ menu "LWIP"
|
||||
This option is used to disable the Network Discovery Protocol (NDP) if it is not required.
|
||||
Please use this option with caution, as the NDP is essential for IPv6 functionality within a local network.
|
||||
|
||||
config LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
bool "Enable API for static IPv6 neighbor cache entries"
|
||||
default n
|
||||
depends on LWIP_ND6
|
||||
help
|
||||
Enable APIs to add/remove permanent IPv6->MAC neighbor cache entries
|
||||
that bypass Neighbor Discovery (no NS/NA). IPv6 counterpart of
|
||||
ETHARP_SUPPORT_STATIC_ENTRIES.
|
||||
|
||||
config LWIP_FORCE_ROUTER_FORWARDING
|
||||
bool "LWIP Force Router Forwarding Enable/Disable"
|
||||
default n
|
||||
|
||||
+1
-1
Submodule components/lwip/lwip updated: 6233a15612...9d2d804124
@@ -1242,6 +1242,19 @@ static inline uint32_t timeout_from_offered(uint32_t lease, uint32_t min)
|
||||
#define LWIP_ND6 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LWIP_ND6_SUPPORT_STATIC_ENTRIES==1: enable nd6_add_static_neighbor() and
|
||||
* nd6_remove_static_neighbor() to manage permanent IPv6->MAC neighbor cache
|
||||
* entries that bypass Neighbor Discovery (no NS/NA). Applied per-(netif,address)
|
||||
* and does not affect NDP on other interfaces. IPv6 counterpart of
|
||||
* ETHARP_SUPPORT_STATIC_ENTRIES.
|
||||
*/
|
||||
#ifdef CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||
#define LWIP_ND6_SUPPORT_STATIC_ENTRIES 1
|
||||
#else
|
||||
#define LWIP_ND6_SUPPORT_STATIC_ENTRIES 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LWIP_FORCE_ROUTER_FORWARDING==1: the router flag in NA packet will always set to 1,
|
||||
* otherwise, never set router flag for NA packets.
|
||||
|
||||
@@ -61,7 +61,10 @@ enum nan_role {
|
||||
* Pairing Initiator NMI || Pairing Responder NMI)).
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 bytes).
|
||||
* @param role enum nan_role value for the local device.
|
||||
* @param role @c NAN_ROLE_PAIRING_INITIATOR or @c NAN_ROLE_PAIRING_RESPONDER.
|
||||
* @param pairing_verification 1 when PASN ran as re-pair verification (NIRA verified on
|
||||
* Auth1 for responder, or @c cfg->pairing_verification on
|
||||
* initiator); 0 for bootstrap auth pairing.
|
||||
* @param ndp_csid NCS-SK CSID for paired-peer NDP (WIFI_NAN_CSID_NCS_SK_128
|
||||
* or _SK_256), 0 if no usable cipher mapping was available.
|
||||
* @param nd_pmk ND-PMK bytes (32) or NULL if KDK was absent.
|
||||
@@ -73,6 +76,7 @@ enum nan_role {
|
||||
*/
|
||||
typedef void (*esp_nan_pairing_key_installed_cb_t)(const uint8_t *peer_nmi,
|
||||
uint8_t role,
|
||||
uint8_t pairing_verification,
|
||||
uint8_t ndp_csid,
|
||||
const uint8_t *nd_pmk,
|
||||
size_t nd_pmk_len,
|
||||
@@ -133,6 +137,23 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
|
||||
uint32_t nik_lifetime_sec,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
|
||||
|
||||
/**
|
||||
* @brief Schedule NAN PASN initiator verification (re-pair with cached NIK/PMK).
|
||||
*
|
||||
* Same role as @ref esp_nan_supp_pasn_initiator_auth but uses PASN verify instead of auth.
|
||||
*
|
||||
* @param peer_nmi Peer NMI (6 bytes).
|
||||
* @param pairing_key_installed_cb Callback invoked after pairwise key installation with peer NMI.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int esp_nan_supp_pasn_initiator_verify(const uint8_t *peer_nmi,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
|
||||
|
||||
/**
|
||||
* @brief Default pairing key-installed callback (NAN app layer).
|
||||
*/
|
||||
esp_nan_pairing_key_installed_cb_t esp_nan_pairing_get_key_installed_cb(void);
|
||||
|
||||
/**
|
||||
* Schedule @ref handle_auth_pasn from NAN app callback table.
|
||||
*/
|
||||
@@ -141,6 +162,20 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
|
||||
const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void);
|
||||
void nan_pasn_clear_saved_keys(void);
|
||||
|
||||
/** Look up cached NPK for pairing verification (§7.6.5). Returns 0 on success.
|
||||
* Lookup matches peer_cred.service_hash against active own-service hashes,
|
||||
* or uses a single cached slot when only one NPK is present. */
|
||||
int nan_global_peer_npk_lookup(uint8_t *npk, size_t *npk_len, int *akmp);
|
||||
|
||||
/**
|
||||
* Mark/clear that the current PASN session for @a peer_nmi is pairing
|
||||
* verification (re-pair) and must not trigger NIK follow-up exchange. The
|
||||
* marker is anchored to own service @a own_inst_id (as resolved from the
|
||||
* verifying NIK), so it survives even when no peer_svc_info exists yet.
|
||||
*/
|
||||
void esp_nan_pairing_mark_verify_session(uint8_t own_inst_id, const uint8_t *peer_nmi);
|
||||
void esp_nan_pairing_clear_verify_session(const uint8_t *peer_nmi);
|
||||
|
||||
/**
|
||||
* Decrypt a NAN Shared Key Descriptor attribute received in a follow-up frame.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "esp_private/esp_supp_nan.h"
|
||||
@@ -17,6 +18,8 @@ struct wpabuf;
|
||||
struct pasn_data;
|
||||
struct rsn_pmksa_cache;
|
||||
|
||||
#define NAN_PASN_PMKID_LEN 16
|
||||
|
||||
typedef esp_nan_pairing_key_installed_cb_t nan_pasn_pairing_key_installed_cb_t;
|
||||
|
||||
struct nan_config {
|
||||
@@ -43,20 +46,20 @@ struct nan_pasn_data {
|
||||
size_t pasn_ptk_len;
|
||||
struct pasn_data *pasn;
|
||||
nan_pasn_pairing_key_installed_cb_t pairing_key_installed_cb;
|
||||
uint8_t pairing_verification;
|
||||
uint32_t nik_lifetime_sec;
|
||||
bool pasn_auth2_done; /**< Initiator: Auth2 already processed (dedup) */
|
||||
uint8_t peer_npkid[NAN_PASN_PMKID_LEN]; /**< Nonce||Tag from peer NIRA; for PMKID cross-check */
|
||||
bool peer_npkid_valid;
|
||||
};
|
||||
|
||||
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr,
|
||||
int freq, int role, const uint8_t *bssid,
|
||||
int role, const uint8_t *bssid,
|
||||
const uint8_t *ssid, size_t ssid_len);
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr, int freq);
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr);
|
||||
struct nan_pasn_data *nan_pasn_data_init(void);
|
||||
void nan_pasn_data_deinit(struct nan_pasn_data *pd);
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq);
|
||||
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr, int freq);
|
||||
int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
const uint8_t *peer_addr, int freq, int role,
|
||||
const uint8_t *bssid,
|
||||
const uint8_t *ssid, size_t ssid_len);
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr);
|
||||
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr);
|
||||
|
||||
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "utils/includes.h"
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "common/nan.h"
|
||||
#include "esp_wifi_driver.h"
|
||||
#include "crypto/crypto.h"
|
||||
@@ -49,12 +50,74 @@
|
||||
|
||||
static struct nan_pasn_key_material g_nan_pasn_saved_keys;
|
||||
|
||||
/* Key index for esp_wifi_set_nan_key_internal (NAN PASN pairwise TK). */
|
||||
int temp = 1;
|
||||
#define NAN_NIRA_NONCE_LEN 8
|
||||
#define NAN_NIRA_TAG_LEN 8
|
||||
#define NAN_NIRA_ATTR_LEN 20
|
||||
#define NAN_PASN_CSIA_ATTR_MAX_LEN (3 + 1 + 2 * 8)
|
||||
#define NAN_PASN_PAIRING_BOOTSTRAP_METHODS WIFI_NAN_BOOTSTRAP_PIN_CODE_DISPLAY
|
||||
#define NAN_PASN_PAIRING_CSIA_PUB_ID 5
|
||||
|
||||
static int nan_pasn_npkid_from_nira_attr(const u8 *nira, u16 nira_len, u8 *npkid)
|
||||
{
|
||||
if (!nira || !npkid ||
|
||||
nira_len < 4 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* NPKID = Nonce || Tag (wire order matches NIRA body after Cipher Version;
|
||||
* PMKID is carried little-endian in the RSNE). */
|
||||
os_memcpy(npkid, nira + 4, NAN_NIRA_NONCE_LEN);
|
||||
os_memcpy(npkid + NAN_NIRA_NONCE_LEN,
|
||||
nira + 4 + NAN_NIRA_NONCE_LEN, NAN_NIRA_TAG_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nan_pasn_build_local_npkid(u8 *npkid)
|
||||
{
|
||||
uint8_t nira_frm[NAN_NIRA_ATTR_LEN];
|
||||
|
||||
if (!npkid ||
|
||||
esp_nan_construct_nira(nira_frm) <
|
||||
(int)(4 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nan_pasn_npkid_from_nira_attr(nira_frm, NAN_NIRA_ATTR_LEN, npkid);
|
||||
}
|
||||
|
||||
static int nan_validate_custom_pmkid(void *ctx, const u8 *addr, const u8 *pmkid)
|
||||
{
|
||||
struct nan_pasn_data *nan = ctx;
|
||||
|
||||
if (!pmkid) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pairing verification: NIK is already checked via NIRA and NPK via
|
||||
* PMKSA lookup, but §7.6.5 also binds RSNE PMKID to the same-frame NIRA
|
||||
* (NPKID = Nonce||Tag). Cross-check when we cached NPKID from that NIRA.
|
||||
*/
|
||||
if (!nan || !nan->peer_npkid_valid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (os_memcmp(pmkid, nan->peer_npkid, PMKID_LEN) != 0) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"NAN PASN verify: PMKID/NPKID mismatch for " MACSTR,
|
||||
MAC2STR(addr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NAN_PASN_AES_WRAP_OVERHEAD 8
|
||||
#define NAN_PASN_AES_WRAP_MIN_CIPHERTEXT (NAN_PASN_AES_WRAP_OVERHEAD + 8)
|
||||
|
||||
static int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
|
||||
|
||||
/**
|
||||
* Map PASN pairwise cipher to the NCS-SK CSID used for paired-peer NDPs
|
||||
* (Wi-Fi Aware v4.0 §7.6.4.2: paired NDP runs NCS-SK style M1-M4, key length
|
||||
@@ -239,7 +302,6 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
|
||||
struct wpa_ptk *ptk;
|
||||
uint8_t key_rsc[8] = {0};
|
||||
int kret;
|
||||
(void)nan;
|
||||
|
||||
if (!pasn) {
|
||||
return -1;
|
||||
@@ -258,7 +320,7 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "NAN PASN: NM-TK", ptk->tk, ptk->tk_len);
|
||||
kret = esp_wifi_set_nan_key_internal(
|
||||
NAN_PASN_WIFI_ALG_CCMP, pasn->peer_addr, temp, 1, key_rsc, sizeof(key_rsc),
|
||||
NAN_PASN_WIFI_ALG_CCMP, pasn->peer_addr, 1, 1, key_rsc, sizeof(key_rsc),
|
||||
ptk->tk, ptk->tk_len,
|
||||
NAN_KEY_NM_TK);
|
||||
if (kret != 0) {
|
||||
@@ -428,6 +490,21 @@ void nan_pasn_clear_saved_keys(void)
|
||||
forced_memzero(&g_nan_pasn_saved_keys, sizeof(g_nan_pasn_saved_keys));
|
||||
}
|
||||
|
||||
static void nan_pasn_clear_peer_tks_for_verify_start(const u8 *peer_nmi)
|
||||
{
|
||||
if (!peer_nmi) {
|
||||
return;
|
||||
}
|
||||
esp_nan_app_clear_peer_tks(peer_nmi, 0);
|
||||
nan_pasn_clear_saved_keys();
|
||||
}
|
||||
|
||||
static void nan_pasn_responder_verify_prepare(const u8 *peer_nmi, uint8_t publish_id)
|
||||
{
|
||||
esp_nan_app_end_peer_datapaths(publish_id);
|
||||
nan_pasn_clear_peer_tks_for_verify_start(peer_nmi);
|
||||
}
|
||||
|
||||
/* NAN KDE OUI Type values from Wi-Fi Aware spec v4.0, Table 126. */
|
||||
#define NAN_PASN_KDE_OUI_TYPE_NIK 36
|
||||
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
|
||||
@@ -749,28 +826,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nan_chan_to_freq_mhz(uint8_t chan)
|
||||
{
|
||||
if (chan >= 1 && chan <= 13) {
|
||||
return 2407 + (int)chan * 5;
|
||||
}
|
||||
if (chan == 14) {
|
||||
return 2484;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nan_pasn_get_current_freq_mhz(void)
|
||||
{
|
||||
uint8_t primary = 0;
|
||||
wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE;
|
||||
|
||||
if (esp_wifi_get_channel(&primary, &second) != ESP_OK || primary == 0) {
|
||||
return 0;
|
||||
}
|
||||
return nan_chan_to_freq_mhz(primary);
|
||||
}
|
||||
|
||||
static void nan_pasn_auth_timeout_cancel(struct nan_pasn_data *nan);
|
||||
|
||||
static void nan_pasn_auth_timeout_cb(void *eloop_ctx, void *user_data)
|
||||
@@ -846,48 +901,97 @@ static int nan_set_dev_sae_pin(struct nan_pasn_data *nan, const char *digits)
|
||||
}
|
||||
|
||||
/*
|
||||
* Build Wi-Fi Alliance NAN vendor IE (EID 221) with DCEA / BPBA / CSIA for PASN
|
||||
* and pass to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends
|
||||
* Build Wi-Fi Alliance NAN vendor IE (EID 221) for PASN.
|
||||
* Bootstrap auth: DCEA + NPBA + CSIA.
|
||||
* Pairing verification: CSIA + NIRA only (no DCEA/NPBA).
|
||||
* Passed to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends
|
||||
* it after PASN Parameters on Auth 1/3 (and after prepare_data_element on Auth 2).
|
||||
*/
|
||||
static int nan_prepare_pasn_extra_ie(struct nan_pasn_data *nan, struct pasn_data *pasn,
|
||||
const struct wpabuf *frame, bool add_dira)
|
||||
const struct wpabuf *frame, bool include_nira)
|
||||
{
|
||||
static const u8 nan_pasn_attr_payload[] = {
|
||||
NAN_ATTR_DCEA, 0x02, 0x00, 0x00, 0x03,
|
||||
NAN_ATTR_BPBA, 0x05, 0x00, 0x90, 0x02, 0x00, 0x02, 0x00,
|
||||
NAN_ATTR_CSIA, 0x03, 0x00, 0x00, 0x07, 0x05,
|
||||
};
|
||||
u8 fixed[2 + 3 + 1 + sizeof(nan_pasn_attr_payload)];
|
||||
u8 csia_attr[NAN_PASN_CSIA_ATTR_MAX_LEN];
|
||||
u8 nira_attr[NAN_NIRA_ATTR_LEN];
|
||||
uint8_t *pairing_attrs = NULL;
|
||||
u8 *buf = NULL;
|
||||
u8 *pos;
|
||||
size_t fr_len = 0;
|
||||
size_t csia_len;
|
||||
size_t nira_len = 0;
|
||||
size_t attr_payload_len;
|
||||
size_t vendor_ie_len;
|
||||
size_t total_len;
|
||||
uint32_t npba_len = 0;
|
||||
uint32_t dcea_len = 0;
|
||||
uint32_t pairing_attrs_len = 0;
|
||||
int ret;
|
||||
|
||||
(void)nan;
|
||||
(void)add_dira;
|
||||
|
||||
fixed[0] = WLAN_EID_VENDOR_SPECIFIC;
|
||||
fixed[1] = 3 + 1 + sizeof(nan_pasn_attr_payload);
|
||||
WPA_PUT_BE24(&fixed[2], OUI_WFA);
|
||||
fixed[5] = NAN_OUI_TYPE;
|
||||
os_memcpy(&fixed[6], nan_pasn_attr_payload, sizeof(nan_pasn_attr_payload));
|
||||
ret = esp_nan_construct_csia(csia_attr, NAN_PASN_PAIRING_CSIA_PUB_ID,
|
||||
WIFI_NAN_CSID_BIT_NCS_PK_PASN_128, 0);
|
||||
if (ret <= 0 || ret > (int)sizeof(csia_attr)) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: CSIA build failed");
|
||||
return -1;
|
||||
}
|
||||
csia_len = (size_t) ret;
|
||||
attr_payload_len = csia_len;
|
||||
|
||||
if (include_nira) {
|
||||
ret = esp_nan_construct_nira(nira_attr);
|
||||
if (ret < (int)NAN_NIRA_ATTR_LEN) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: NIRA build failed");
|
||||
return -1;
|
||||
}
|
||||
nira_len = (size_t) ret;
|
||||
attr_payload_len += nira_len;
|
||||
} else {
|
||||
pairing_attrs = esp_wifi_nan_get_pairing_attrs(NAN_PASN_PAIRING_BOOTSTRAP_METHODS,
|
||||
true, true, &npba_len,
|
||||
&dcea_len, &pairing_attrs_len);
|
||||
if (!pairing_attrs || !pairing_attrs_len) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: pairing attrs build failed");
|
||||
return -1;
|
||||
}
|
||||
attr_payload_len += pairing_attrs_len;
|
||||
}
|
||||
|
||||
if (frame) {
|
||||
fr_len = wpabuf_len(frame);
|
||||
}
|
||||
|
||||
if (!fr_len) {
|
||||
return pasn_set_extra_ies(pasn, fixed, sizeof(fixed));
|
||||
vendor_ie_len = 3 + 1 + attr_payload_len;
|
||||
if (vendor_ie_len > UINT8_MAX) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: extra IE too long");
|
||||
return -1;
|
||||
}
|
||||
|
||||
total_len = sizeof(fixed) + fr_len;
|
||||
total_len = 2 + vendor_ie_len + fr_len;
|
||||
buf = os_malloc(total_len);
|
||||
if (!buf) {
|
||||
return -1;
|
||||
}
|
||||
os_memcpy(buf, fixed, sizeof(fixed));
|
||||
os_memcpy(buf + sizeof(fixed), wpabuf_head_u8(frame), fr_len);
|
||||
|
||||
pos = buf;
|
||||
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
||||
*pos++ = (u8) vendor_ie_len;
|
||||
WPA_PUT_BE24(pos, OUI_WFA);
|
||||
pos += 3;
|
||||
*pos++ = NAN_OUI_TYPE;
|
||||
if (pairing_attrs_len) {
|
||||
os_memcpy(pos, pairing_attrs, pairing_attrs_len);
|
||||
pos += pairing_attrs_len;
|
||||
}
|
||||
os_memcpy(pos, csia_attr, csia_len);
|
||||
pos += csia_len;
|
||||
if (nira_len) {
|
||||
os_memcpy(pos, nira_attr, nira_len);
|
||||
pos += nira_len;
|
||||
}
|
||||
if (fr_len) {
|
||||
os_memcpy(pos, wpabuf_head_u8(frame), fr_len);
|
||||
}
|
||||
|
||||
ret = pasn_set_extra_ies(pasn, buf, total_len);
|
||||
os_free(buf);
|
||||
return ret;
|
||||
@@ -962,7 +1066,7 @@ static void nan_pairing_apply_sae_pin(struct pasn_data *pasn, u8 pasn_type,
|
||||
pasn->password = nan->dev_sae_pin;
|
||||
}
|
||||
|
||||
void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bool verify, bool derive_kek)
|
||||
void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr)
|
||||
{
|
||||
struct pasn_data *pasn;
|
||||
struct wpabuf *rsnxe;
|
||||
@@ -971,6 +1075,8 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
|
||||
return;
|
||||
}
|
||||
|
||||
nan->peer_npkid_valid = false;
|
||||
|
||||
if (nan->pasn) {
|
||||
wpa_pasn_reset(nan->pasn);
|
||||
} else {
|
||||
@@ -1021,15 +1127,13 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
|
||||
* ND-PMK is filled by pasn_nd_pmk_derive_from_kdk_store (hostap
|
||||
* nan_crypto_derive_nd_pmk_from_kdk). Matches hostap nan_pairing.c.
|
||||
*/
|
||||
(void)derive_kek;
|
||||
pasn->derive_kek = false;
|
||||
pasn->kek_len = 0;
|
||||
|
||||
/* Wi-Fi Aware pairing: PASN Auth frames always use SAE as the base AKM. */
|
||||
pasn->akmp = WPA_KEY_MGMT_SAE;
|
||||
if (nan->dev_sae_pin_len > 0) {
|
||||
pasn->akmp = WPA_KEY_MGMT_SAE;
|
||||
nan_pairing_apply_sae_pin(pasn, nan->cfg->pasn_type, nan);
|
||||
} else if (!verify) {
|
||||
pasn->akmp = WPA_KEY_MGMT_PASN;
|
||||
}
|
||||
|
||||
pasn->rsn_pairwise = pasn->cipher;
|
||||
@@ -1059,16 +1163,19 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
|
||||
pasn->parse_data_element = nan->cfg->parse_data_element;
|
||||
pasn->validate_custom_pmkid = nan->cfg->pasn_validate_pmkid;
|
||||
|
||||
pasn->freq = freq;
|
||||
|
||||
}
|
||||
|
||||
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr,
|
||||
int freq, int role,
|
||||
int role,
|
||||
const u8 *bssid, const u8 *ssid, size_t ssid_len)
|
||||
{
|
||||
struct nan_pasn_data *nan;
|
||||
struct pasn_data *pasn;
|
||||
uint8_t npk[NAN_PASN_KEY_PMK_MAX];
|
||||
uint8_t own_addr[ETH_ALEN];
|
||||
u8 npkid[PMKID_LEN];
|
||||
size_t npk_len = 0;
|
||||
int akmp = WPA_KEY_MGMT_SAE;
|
||||
int ret = 0;
|
||||
|
||||
(void)role;
|
||||
@@ -1087,29 +1194,55 @@ int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr,
|
||||
}
|
||||
|
||||
nan->dev_role = NAN_ROLE_PAIRING_INITIATOR;
|
||||
nan_pasn_initialize(nan, peer_addr, freq, true, true);
|
||||
nan->pasn_auth2_done = false;
|
||||
nan->pairing_verification = 1;
|
||||
nan_pasn_clear_peer_tks_for_verify_start(peer_addr);
|
||||
nan_pasn_initialize(nan, peer_addr);
|
||||
pasn = nan->pasn;
|
||||
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN: extra IE failed");
|
||||
if (!pasn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nan->cfg->pasn_validate_pmkid = nan_validate_custom_pmkid;
|
||||
pasn->validate_custom_pmkid = nan_validate_custom_pmkid;
|
||||
|
||||
/* §7.6.5: seed PMKSA with cached NPK and local NPKID (Nonce||Tag). */
|
||||
if (nan_global_peer_npk_lookup(npk, &npk_len, &akmp) != 0 ||
|
||||
esp_wifi_get_mac(WIFI_IF_NAN, own_addr) != ESP_OK ||
|
||||
nan_pasn_build_local_npkid(npkid) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pasn_initiator_pmksa_cache_add(nan->initiator_pmksa, own_addr,
|
||||
(u8 *) peer_addr, npk, npk_len, npkid) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pasn_set_akmp(pasn, akmp);
|
||||
pasn->wpa_key_mgmt = akmp;
|
||||
pasn_set_custom_pmkid(pasn, npkid);
|
||||
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, true) != 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
|
||||
pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
|
||||
pasn->akmp, pasn->cipher, pasn->group, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
wpa_printf(MSG_INFO, "PASN verify failed");
|
||||
ret = -1;
|
||||
}
|
||||
if (pasn->extra_ies) {
|
||||
os_free((u8 *) pasn->extra_ies);
|
||||
pasn->extra_ies = NULL;
|
||||
pasn->extra_ies_len = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
forced_memzero(npk, sizeof(npk));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
|
||||
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr)
|
||||
{
|
||||
struct nan_pasn_data *nan;
|
||||
struct pasn_data *pasn;
|
||||
@@ -1126,10 +1259,16 @@ int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
|
||||
}
|
||||
|
||||
nan->dev_role = NAN_ROLE_PAIRING_INITIATOR;
|
||||
nan->pasn_auth2_done = false;
|
||||
nan->pairing_verification = 0;
|
||||
|
||||
nan_pasn_initialize(nan, addr, freq, false, true);
|
||||
nan_pasn_initialize(nan, addr);
|
||||
pasn = nan->pasn;
|
||||
|
||||
if (!pasn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr);
|
||||
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) {
|
||||
@@ -1138,7 +1277,7 @@ int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
|
||||
}
|
||||
|
||||
if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
|
||||
pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
|
||||
pasn->akmp, pasn->cipher, pasn->group, 0,
|
||||
NULL, 0, NULL, 0, NULL)) {
|
||||
wpa_printf(MSG_INFO, "Failed to start PASN");
|
||||
ret = -1;
|
||||
@@ -1167,15 +1306,13 @@ int * int_array_dup(const int *a)
|
||||
}
|
||||
|
||||
static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
const struct ieee80211_auth *mgmt, size_t len,
|
||||
int freq)
|
||||
const struct ieee80211_auth *mgmt, size_t len)
|
||||
{
|
||||
struct pasn_data *pasn;
|
||||
u8 pasn_type;
|
||||
int pasn_groups[4] = { 0 };
|
||||
u16 auth_alg, auth_transaction, status_code;
|
||||
|
||||
(void)freq;
|
||||
if (!nan || !nan->pasn) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1246,6 +1383,7 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
nan_pasn_store_ptk(nan, &pasn->ptk);
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
nan_pasn_copy_keys_from_pasn(nan, pasn);
|
||||
/* Install NM-TK only after Auth3 is successfully validated. */
|
||||
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
|
||||
nan->pairing_key_installed_cb) {
|
||||
const uint8_t *nd_pmk = pasn_nd_pmk_global.valid ?
|
||||
@@ -1253,6 +1391,7 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
size_t nd_pmk_len = pasn_nd_pmk_global.valid ? PMK_LEN : 0;
|
||||
nan->pairing_key_installed_cb(pasn->peer_addr,
|
||||
(uint8_t)nan->dev_role,
|
||||
nan->pairing_verification,
|
||||
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
|
||||
nd_pmk, nd_pmk_len,
|
||||
nan->nik_lifetime_sec);
|
||||
@@ -1263,8 +1402,108 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a NIRA attribute inside a WFA NAN vendor-specific IE (OUI + type 0x13).
|
||||
* Returns a pointer to the attribute (ID + length + body) or NULL.
|
||||
*/
|
||||
static const u8 *nan_pasn_find_nira_attr(const u8 *ies, size_t ies_len, u16 *attr_len)
|
||||
{
|
||||
const u8 *ie = ies;
|
||||
const u8 *end = ies + ies_len;
|
||||
|
||||
while (ie + 2 <= end) {
|
||||
u8 id = ie[0];
|
||||
u8 elen = ie[1];
|
||||
|
||||
if (ie + 2 + elen > end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4 &&
|
||||
WPA_GET_BE24(ie + 2) == OUI_WFA && ie[5] == NAN_OUI_TYPE) {
|
||||
const u8 *pos = ie + 6;
|
||||
const u8 *attrs_end = ie + 2 + elen;
|
||||
|
||||
while (attrs_end - pos >= NAN_ATTR_HDR_LEN) {
|
||||
u8 attr_id = pos[0];
|
||||
u16 attr_body_len = WPA_GET_LE16(pos + 1);
|
||||
u16 total_attr_len;
|
||||
|
||||
if (attr_body_len > (size_t)(attrs_end - pos - NAN_ATTR_HDR_LEN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
total_attr_len = NAN_ATTR_HDR_LEN + attr_body_len;
|
||||
if (attr_id == NAN_ATTR_NIRA) {
|
||||
if (attr_len) {
|
||||
*attr_len = total_attr_len;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
pos += total_attr_len;
|
||||
}
|
||||
}
|
||||
ie += 2 + elen;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find NIRA in @a mgmt once, verify identity, and stash NPKID for PMKID
|
||||
* cross-check. When @a own_inst_id is non-NULL the matched own service id is
|
||||
* also returned (0 if none). */
|
||||
static bool nan_pasn_auth_process_nira(struct nan_pasn_data *nan,
|
||||
const u8 *peer_addr,
|
||||
const struct ieee80211_auth *mgmt,
|
||||
size_t len, u16 auth_trans,
|
||||
uint8_t *own_inst_id)
|
||||
{
|
||||
const u8 *var;
|
||||
size_t var_len;
|
||||
const u8 *nira;
|
||||
u16 nira_len = 0;
|
||||
|
||||
if (own_inst_id) {
|
||||
*own_inst_id = 0;
|
||||
}
|
||||
|
||||
if (!nan || !peer_addr || !mgmt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nan->peer_npkid_valid = false;
|
||||
|
||||
if (len < offsetof(struct ieee80211_auth, auth.variable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var = mgmt->auth.variable;
|
||||
var_len = len - offsetof(struct ieee80211_auth, auth.variable);
|
||||
nira = nan_pasn_find_nira_attr(var, var_len, &nira_len);
|
||||
if (!nira) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: NIRA missing in Auth%u from "
|
||||
MACSTR, auth_trans, MAC2STR(peer_addr));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!esp_nan_verify_nira_get_own_svc((u8 *) peer_addr, (u8 *) nira, nira_len,
|
||||
own_inst_id)) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: NIRA verification failed in "
|
||||
"Auth%u for " MACSTR, auth_trans, MAC2STR(peer_addr));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nan_pasn_npkid_from_nira_attr(nira, nira_len, nan->peer_npkid) == 0) {
|
||||
nan->peer_npkid_valid = true;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "NAN PASN verify: NIRA OK in Auth%u for " MACSTR,
|
||||
auth_trans, MAC2STR(peer_addr));
|
||||
return true;
|
||||
}
|
||||
|
||||
int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgmt,
|
||||
size_t len, int freq)
|
||||
size_t len)
|
||||
{
|
||||
int ret = 0;
|
||||
u16 auth_transaction;
|
||||
@@ -1293,6 +1532,23 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
|
||||
|
||||
if (nan->dev_role == NAN_ROLE_PAIRING_INITIATOR &&
|
||||
auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) {
|
||||
/*
|
||||
* Duplicate Auth2: responder may retransmit if it didn't get Auth3
|
||||
* ACK. wpa_pasn_auth_rx() would reject with trans_seq mismatch and
|
||||
* we would also lose NM-TK that was already installed; silently
|
||||
* accept the retransmit instead.
|
||||
*/
|
||||
if (nan->pasn_auth2_done) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pasn->custom_pmkid_valid &&
|
||||
!nan_pasn_auth_process_nira(nan, mgmt->sa, mgmt, len, 2, NULL)) {
|
||||
wpa_printf(MSG_INFO, "PASN: Auth2 NIRA verify failed");
|
||||
nan->dev_role = NAN_ROLE_IDLE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
|
||||
if (ret < 0) {
|
||||
wpa_printf(MSG_INFO, "PASN: wpa_pasn_auth_rx() failed");
|
||||
@@ -1304,13 +1560,15 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
|
||||
* successfully built/transmitted by wpa_pasn_auth_rx().
|
||||
*/
|
||||
nan_pasn_copy_keys_from_pasn(nan, pasn);
|
||||
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
|
||||
nan->pairing_key_installed_cb) {
|
||||
nan->pasn_auth2_done = true;
|
||||
int tk_ret = nan_pasn_install_nan_pairwise_tk(nan, pasn);
|
||||
if (tk_ret == 0 && nan->pairing_key_installed_cb) {
|
||||
const uint8_t *nd_pmk = pasn_nd_pmk_global.valid ?
|
||||
pasn_nd_pmk_global.nd_pmk : NULL;
|
||||
size_t nd_pmk_len = pasn_nd_pmk_global.valid ? PMK_LEN : 0;
|
||||
nan->pairing_key_installed_cb(pasn->peer_addr,
|
||||
(uint8_t)nan->dev_role,
|
||||
nan->pairing_verification,
|
||||
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
|
||||
nd_pmk, nd_pmk_len,
|
||||
nan->nik_lifetime_sec);
|
||||
@@ -1321,7 +1579,7 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
|
||||
#endif /* CONFIG_TESTING_OPTIONS */
|
||||
forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
|
||||
} else {
|
||||
ret = nan_handle_pasn_auth(nan, mgmt, len, freq);
|
||||
ret = nan_handle_pasn_auth(nan, mgmt, len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1330,7 +1588,8 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
|
||||
{
|
||||
const struct ieee80211_auth *mgmt;
|
||||
struct nan_pasn_data *nan;
|
||||
int rx_freq;
|
||||
bool auth1_verify = false;
|
||||
u8 auth1_pmkid[PMKID_LEN];
|
||||
|
||||
(void)trans_seq;
|
||||
(void)status;
|
||||
@@ -1340,15 +1599,114 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
|
||||
}
|
||||
mgmt = (const struct ieee80211_auth *)buf;
|
||||
nan = esp_nan_app_get_pasn_data();
|
||||
rx_freq = nan_pasn_get_current_freq_mhz();
|
||||
if (rx_freq <= 0) {
|
||||
rx_freq = 2412;
|
||||
if (!nan) {
|
||||
/*
|
||||
* Only lazy-init on Auth1: a stray Auth2/Auth3 (e.g. retransmit after
|
||||
* we already completed PASN and tore down the session) must be
|
||||
* dropped, otherwise we would init a fresh context and try to process
|
||||
* a mid-handshake frame against it, which fails noisily.
|
||||
*/
|
||||
if (le_to_host16(mgmt->auth.auth_transaction) !=
|
||||
WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
|
||||
return;
|
||||
}
|
||||
if (pasn_responder_init(mgmt->sa, UINT32_MAX,
|
||||
esp_nan_pairing_get_key_installed_cb()) != 0) {
|
||||
return;
|
||||
}
|
||||
nan = esp_nan_app_get_pasn_data();
|
||||
}
|
||||
if (!nan) {
|
||||
wpa_printf(MSG_DEBUG, "NAN PASN: receive_pasn: no context");
|
||||
return;
|
||||
}
|
||||
nan_pasn_auth_rx(nan, mgmt, len, rx_freq);
|
||||
|
||||
if (le_to_host16(mgmt->auth.auth_transaction) == 1) {
|
||||
struct ieee802_11_elems elems;
|
||||
struct wpa_ie_data rsn_data;
|
||||
struct wpa_pasn_params_data pasn_params;
|
||||
|
||||
if (ieee802_11_parse_elems(mgmt->auth.variable,
|
||||
len - offsetof(struct ieee80211_auth, auth.variable),
|
||||
&elems, 0) != ParseFailed &&
|
||||
elems.rsn_ie && elems.pasn_params &&
|
||||
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
|
||||
&rsn_data) == 0 &&
|
||||
rsn_data.num_pmkid &&
|
||||
wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
|
||||
elems.pasn_params_len + 3,
|
||||
false, &pasn_params) == 0 &&
|
||||
pasn_params.wrapped_data_format == WPA_PASN_WRAPPED_DATA_NO) {
|
||||
auth1_verify = true;
|
||||
os_memcpy(auth1_pmkid, rsn_data.pmkid, PMKID_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
if (auth1_verify) {
|
||||
uint8_t npk[NAN_PASN_KEY_PMK_MAX];
|
||||
uint8_t own_addr[ETH_ALEN];
|
||||
size_t npk_len = 0;
|
||||
int akmp = WPA_KEY_MGMT_SAE;
|
||||
struct pasn_data *pasn = nan->pasn;
|
||||
bool auth1_ok = false;
|
||||
uint8_t verify_own_inst_id = 0;
|
||||
|
||||
if (!nan_pasn_auth_process_nira(nan, mgmt->sa, mgmt, len, 1,
|
||||
&verify_own_inst_id)) {
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
nan_pasn_responder_verify_prepare(mgmt->sa, verify_own_inst_id);
|
||||
|
||||
if (!pasn) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: no PASN context for "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
if (nan_global_peer_npk_lookup(npk, &npk_len, &akmp) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: no cached NPK for "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
if (esp_wifi_get_mac(WIFI_IF_NAN, own_addr) != ESP_OK) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: failed to read NAN NMI");
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
if (pasn_responder_pmksa_cache_add(nan->responder_pmksa, own_addr,
|
||||
(u8 *) mgmt->sa, npk, npk_len,
|
||||
auth1_pmkid) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: PMKSA cache add failed for "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
|
||||
{
|
||||
u8 npkid[PMKID_LEN];
|
||||
|
||||
nan->cfg->pasn_validate_pmkid = nan_validate_custom_pmkid;
|
||||
pasn->validate_custom_pmkid = nan_validate_custom_pmkid;
|
||||
pasn_set_akmp(pasn, akmp);
|
||||
pasn->wpa_key_mgmt = akmp;
|
||||
if (nan_pasn_build_local_npkid(npkid) == 0) {
|
||||
pasn_set_custom_pmkid(pasn, npkid);
|
||||
}
|
||||
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, true) != 0) {
|
||||
wpa_printf(MSG_INFO, "NAN PASN verify: extra IE (NIRA) build "
|
||||
"failed for " MACSTR, MAC2STR(mgmt->sa));
|
||||
goto auth1_verify_done;
|
||||
}
|
||||
auth1_ok = true;
|
||||
nan->pairing_verification = 1;
|
||||
}
|
||||
|
||||
auth1_verify_done:
|
||||
forced_memzero(npk, sizeof(npk));
|
||||
if (!auth1_ok) {
|
||||
return;
|
||||
}
|
||||
esp_nan_pairing_mark_verify_session(verify_own_inst_id, mgmt->sa);
|
||||
}
|
||||
|
||||
nan_pasn_auth_rx(nan, mgmt, len);
|
||||
}
|
||||
|
||||
void nan_pasn_pmksa_set_pmk(struct nan_pasn_data *nan, const u8 *src, const u8 *dst,
|
||||
@@ -1539,16 +1897,14 @@ void nan_pasn_data_deinit(struct nan_pasn_data *pd)
|
||||
os_free(pd);
|
||||
}
|
||||
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq)
|
||||
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr)
|
||||
{
|
||||
if (!pd || !peer_addr) {
|
||||
return -1;
|
||||
}
|
||||
return nan_initiate_pasn_auth(pd, peer_addr, freq);
|
||||
return nan_initiate_pasn_auth(pd, peer_addr);
|
||||
}
|
||||
|
||||
#define NAN_PASN_VERIFY_ELOOP_SSID_MAX 32
|
||||
|
||||
struct nan_pasn_eloop_ctx {
|
||||
uint8_t peer_addr[ETH_ALEN];
|
||||
uint32_t pincode;
|
||||
@@ -1556,10 +1912,37 @@ struct nan_pasn_eloop_ctx {
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb;
|
||||
};
|
||||
|
||||
static struct nan_pasn_data *nan_pasn_eloop_prepare(struct nan_pasn_eloop_ctx *ctx,
|
||||
const char *pin_to_apply)
|
||||
{
|
||||
struct nan_pasn_data *old;
|
||||
struct nan_pasn_data *pd;
|
||||
|
||||
old = esp_nan_app_get_pasn_data();
|
||||
if (old) {
|
||||
nan_pasn_data_deinit(old);
|
||||
}
|
||||
|
||||
pd = nan_pasn_data_init();
|
||||
if (!pd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_nan_app_set_pasn_data(pd);
|
||||
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
|
||||
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
|
||||
|
||||
if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) {
|
||||
nan_pasn_data_deinit(pd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
{
|
||||
struct nan_pasn_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_data *old;
|
||||
struct nan_pasn_data *pd;
|
||||
char pin_digits[16];
|
||||
int n;
|
||||
@@ -1570,46 +1953,23 @@ static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
return;
|
||||
}
|
||||
|
||||
old = esp_nan_app_get_pasn_data();
|
||||
if (old) {
|
||||
nan_pasn_data_deinit(old);
|
||||
}
|
||||
|
||||
pd = nan_pasn_data_init();
|
||||
if (!pd) {
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_nan_app_set_pasn_data(pd);
|
||||
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
|
||||
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
|
||||
|
||||
if (ctx->pincode != UINT32_MAX) {
|
||||
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
|
||||
(unsigned)ctx->pincode);
|
||||
if (os_snprintf_error(sizeof(pin_digits), n)) {
|
||||
nan_pasn_data_deinit(pd);
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
pin_to_apply = pin_digits;
|
||||
}
|
||||
|
||||
if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) {
|
||||
nan_pasn_data_deinit(pd);
|
||||
pd = nan_pasn_eloop_prepare(ctx, pin_to_apply);
|
||||
if (!pd) {
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
int freq = nan_pasn_get_current_freq_mhz();
|
||||
|
||||
if (freq <= 0) {
|
||||
freq = 2412;
|
||||
}
|
||||
nan_pasn_auth_initiate(pd, ctx->peer_addr, freq);
|
||||
}
|
||||
nan_pasn_auth_initiate(pd, ctx->peer_addr);
|
||||
os_free(ctx);
|
||||
}
|
||||
|
||||
@@ -1619,8 +1979,12 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
|
||||
{
|
||||
struct nan_pasn_eloop_ctx *ctx;
|
||||
|
||||
if (!peer_nmi) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx = os_zalloc(sizeof(*ctx));
|
||||
if (!ctx || !peer_nmi) {
|
||||
if (!ctx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1637,49 +2001,32 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nan_pasn_verify_eloop_ctx {
|
||||
uint8_t peer_addr[ETH_ALEN];
|
||||
int freq;
|
||||
int role;
|
||||
uint8_t bssid[ETH_ALEN];
|
||||
uint8_t ssid[NAN_PASN_VERIFY_ELOOP_SSID_MAX];
|
||||
size_t ssid_len;
|
||||
};
|
||||
|
||||
static void nan_pasn_verify_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
static void nan_pasn_verify_init_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
{
|
||||
struct nan_pasn_verify_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_data *pd;
|
||||
const uint8_t *ssid_arg;
|
||||
|
||||
(void)eloop_ctx;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
pd = esp_nan_app_get_pasn_data();
|
||||
pd = nan_pasn_eloop_prepare(ctx, NULL);
|
||||
if (!pd) {
|
||||
os_free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ssid_arg = ctx->ssid_len ? ctx->ssid : NULL;
|
||||
nan_initiate_pasn_verify(pd, ctx->peer_addr, ctx->freq, ctx->role,
|
||||
ctx->bssid, ssid_arg, ctx->ssid_len);
|
||||
nan_initiate_pasn_verify(pd, ctx->peer_addr, 0, NULL, NULL, 0);
|
||||
os_free(ctx);
|
||||
}
|
||||
|
||||
int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
const uint8_t *peer_addr, int freq, int role,
|
||||
const uint8_t *bssid,
|
||||
const uint8_t *ssid, size_t ssid_len)
|
||||
int esp_nan_supp_pasn_initiator_verify(const uint8_t *peer_nmi,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
|
||||
{
|
||||
struct nan_pasn_verify_eloop_ctx *ctx;
|
||||
struct nan_pasn_eloop_ctx *ctx;
|
||||
|
||||
if (!peer_addr) {
|
||||
return -1;
|
||||
}
|
||||
if (ssid_len > NAN_PASN_VERIFY_ELOOP_SSID_MAX) {
|
||||
if (!peer_nmi) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1688,20 +2035,10 @@ int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_memcpy(ctx->peer_addr, peer_addr, ETH_ALEN);
|
||||
ctx->freq = freq;
|
||||
ctx->role = role;
|
||||
if (bssid) {
|
||||
os_memcpy(ctx->bssid, bssid, ETH_ALEN);
|
||||
} else {
|
||||
os_memcpy(ctx->bssid, peer_addr, ETH_ALEN);
|
||||
}
|
||||
if (ssid && ssid_len) {
|
||||
os_memcpy(ctx->ssid, ssid, ssid_len);
|
||||
ctx->ssid_len = ssid_len;
|
||||
}
|
||||
os_memcpy(ctx->peer_addr, peer_nmi, ETH_ALEN);
|
||||
ctx->pairing_key_installed_cb = pairing_key_installed_cb;
|
||||
|
||||
if (eloop_register_timeout(secs, usecs, nan_pasn_verify_eloop_cb, NULL, ctx) != 0) {
|
||||
if (eloop_register_timeout(0, 0, nan_pasn_verify_init_eloop_cb, NULL, ctx) != 0) {
|
||||
os_free(ctx);
|
||||
return -1;
|
||||
}
|
||||
@@ -1717,9 +2054,11 @@ int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
|
||||
*
|
||||
* @param peer_addr Peer NAN address, or NULL to use a broadcast placeholder until Auth1.
|
||||
* @param pincode 6-digit value 0..999999, or @c UINT32_MAX to keep default PIN from @ref nan_pasn_data_init.
|
||||
* @param pairing_key_installed_cb Callback stored on the responder PASN context.
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode,
|
||||
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
|
||||
{
|
||||
struct nan_pasn_data *pd;
|
||||
struct nan_pasn_data *old;
|
||||
@@ -1728,11 +2067,20 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
const u8 *peer = peer_addr ? peer_addr : bcast;
|
||||
const char *pin_to_apply = NULL;
|
||||
int n;
|
||||
int freq;
|
||||
|
||||
os_memset(pin_digits, 0, sizeof(pin_digits));
|
||||
|
||||
old = esp_nan_app_get_pasn_data();
|
||||
/* Lazy Auth1 init and scheduled bootstrap init can both call this; do not
|
||||
* tear down an in-progress responder session for the same peer. */
|
||||
if (old && old->dev_role == NAN_ROLE_PAIRING_RESPONDER && old->pasn &&
|
||||
!is_broadcast_ether_addr(peer) &&
|
||||
os_memcmp(old->pasn->peer_addr, peer, ETH_ALEN) == 0) {
|
||||
if (pairing_key_installed_cb) {
|
||||
old->pairing_key_installed_cb = pairing_key_installed_cb;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (old) {
|
||||
nan_pasn_data_deinit(old);
|
||||
}
|
||||
@@ -1743,6 +2091,7 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
}
|
||||
|
||||
esp_nan_app_set_pasn_data(pd);
|
||||
pd->pairing_key_installed_cb = pairing_key_installed_cb;
|
||||
|
||||
if (pincode != UINT32_MAX) {
|
||||
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
|
||||
@@ -1757,13 +2106,8 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
freq = nan_pasn_get_current_freq_mhz();
|
||||
if (freq <= 0) {
|
||||
freq = 2412;
|
||||
}
|
||||
|
||||
pd->dev_role = NAN_ROLE_PAIRING_RESPONDER;
|
||||
nan_pasn_initialize(pd, peer, freq, false, true);
|
||||
nan_pasn_initialize(pd, peer);
|
||||
|
||||
if (!pd->pasn || nan_prepare_pasn_extra_ie(pd, pd->pasn, NULL, false) != 0) {
|
||||
goto fail;
|
||||
@@ -1786,16 +2130,15 @@ struct pasn_responder_eloop_ctx {
|
||||
static void pasn_responder_init_eloop_cb(void *eloop_ctx, void *user_data)
|
||||
{
|
||||
struct pasn_responder_eloop_ctx *ctx = user_data;
|
||||
struct nan_pasn_data *pd;
|
||||
|
||||
(void)eloop_ctx;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
if (pasn_responder_init(ctx->peer_addr, ctx->pincode) == 0) {
|
||||
pd = esp_nan_app_get_pasn_data();
|
||||
if (pasn_responder_init(ctx->peer_addr, ctx->pincode,
|
||||
ctx->pairing_key_installed_cb) == 0) {
|
||||
struct nan_pasn_data *pd = esp_nan_app_get_pasn_data();
|
||||
if (pd) {
|
||||
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
|
||||
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +238,8 @@ typedef enum {
|
||||
NAN_KEY_ND_TK = 0,
|
||||
NAN_KEY_ND_GTK,
|
||||
NAN_KEY_NM_TK,
|
||||
NAN_KEY_ND_IGTK, /* 3 - NAN Integrity Group Temporal Key (BIP-CMAC-128) */
|
||||
NAN_KEY_ND_BIGTK, /* 4 - NAN Beacon Integrity Group Temporal Key (BIP-CMAC-128) */
|
||||
} nan_key_type_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -341,7 +343,10 @@ 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);
|
||||
uint8_t esp_wifi_ap_get_owe_config_internal(void);
|
||||
esp_err_t esp_nan_complete_pairing(uint8_t svc_id, uint8_t peer_svc_id);
|
||||
esp_err_t esp_nan_set_pairing_status(uint8_t svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[6], bool pairing_complete);
|
||||
uint8_t *esp_wifi_nan_get_pairing_attrs(uint16_t bootstrap_methods, bool pairing_enabled,
|
||||
bool nik_cache_enabled, uint32_t *npba_len,
|
||||
uint32_t *dcea_len, uint32_t *total_len);
|
||||
esp_err_t esp_wifi_nan_load_saved_creds(uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN], bool *own_nik_valid,
|
||||
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS], uint8_t *num_peer_creds);
|
||||
esp_err_t esp_wifi_nan_save_own_nik(const uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN]);
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
#define IEEE802_11_H
|
||||
|
||||
enum wpa_validate_result;
|
||||
int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr);
|
||||
#ifdef CONFIG_SAE
|
||||
int auth_sae_queue(struct hostapd_data *hapd, u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction);
|
||||
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
|
||||
u8 *buf, size_t len, u8 *bssid,
|
||||
u16 auth_transaction, u16 status);
|
||||
#endif /* CONFIG_SAE */
|
||||
u16 wpa_res_to_status_code(enum wpa_validate_result res);
|
||||
#ifdef CONFIG_OWE_SOFTAP
|
||||
uint16_t owe_process_assoc_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh,
|
||||
|
||||
@@ -52,7 +52,8 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||
unsigned int hash;
|
||||
|
||||
pmksa->pmksa_count--;
|
||||
pmksa->free_cb(entry, pmksa->ctx);
|
||||
if (pmksa->free_cb)
|
||||
pmksa->free_cb(entry, pmksa->ctx);
|
||||
|
||||
/* unlink from hash list */
|
||||
hash = PMKID_HASH(entry->pmkid);
|
||||
|
||||
@@ -48,7 +48,7 @@ enum nan_attr_id {
|
||||
NAN_ATTR_NDP_EXT = 0x29,
|
||||
NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */
|
||||
NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */
|
||||
NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
|
||||
NAN_ATTR_NPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
|
||||
NAN_ATTR_S3 = 0x2D,
|
||||
NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */
|
||||
NAN_ATTR_VENDOR_SPECIFIC = 0xDD,
|
||||
|
||||
@@ -624,6 +624,9 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
|
||||
#else /* CONFIG_IEEE80211R */
|
||||
goto fail;
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
} else if (verify && pasn->custom_pmkid_valid) {
|
||||
/* Wi-Fi Aware pairing verification: NPKID in RSNE, no wrapped data */
|
||||
pmkid = pasn->custom_pmkid;
|
||||
} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
|
||||
struct rsn_pmksa_cache_entry *pmksa;
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
|
||||
enum pmksa_free_reason reason)
|
||||
{
|
||||
pmksa->pmksa_count--;
|
||||
pmksa->free_cb(entry, pmksa->ctx, reason);
|
||||
if (pmksa->free_cb)
|
||||
pmksa->free_cb(entry, pmksa->ctx, reason);
|
||||
_pmksa_cache_free_entry(entry);
|
||||
}
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
|
||||
MAC2STR(dest));
|
||||
}
|
||||
#else
|
||||
return ret;
|
||||
goto out;
|
||||
#endif
|
||||
}
|
||||
if (key_mic &&
|
||||
@@ -263,8 +263,13 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
|
||||
wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
|
||||
wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, wpa_mic_len(sm->key_mgmt, sm->pmk_len));
|
||||
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
|
||||
return wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
|
||||
ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
|
||||
out:
|
||||
#ifdef ESP_SUPPLICANT
|
||||
wpa_sm_free_eapol(msg);
|
||||
#else
|
||||
os_free(msg);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -338,7 +343,6 @@ static void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
|
||||
error, pairwise, sm->ptk_set, (unsigned long) rlen);
|
||||
wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, wpa_sm_get_auth_addr(sm),
|
||||
ETH_P_EAPOL, rbuf, rlen, key_mic);
|
||||
wpa_sm_free_eapol(rbuf);
|
||||
}
|
||||
|
||||
static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
|
||||
@@ -680,11 +684,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPA Send EAPOL-Key 2/4");
|
||||
|
||||
wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
|
||||
return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
|
||||
rbuf, rlen, key_mic);
|
||||
wpa_sm_free_eapol(rbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
|
||||
@@ -1353,11 +1354,8 @@ static int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *ds
|
||||
WPA_PUT_BE16(reply->key_data_length, 0);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPA Send EAPOL-Key 4/4");
|
||||
wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
|
||||
return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
|
||||
rbuf, rlen, key_mic);
|
||||
wpa_sm_free_eapol(rbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wpa_sm_set_seq(struct wpa_sm *sm, struct wpa_eapol_key *key, u8 isptk)
|
||||
@@ -1869,11 +1867,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
|
||||
|
||||
wpa_printf(MSG_DEBUG, "WPA Send 2/2 Group key");
|
||||
|
||||
wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, ETH_P_EAPOL,
|
||||
return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, ETH_P_EAPOL,
|
||||
rbuf, rlen, key_mic);
|
||||
wpa_sm_free_eapol(rbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
|
||||
|
||||
@@ -35,6 +35,16 @@ menu "Example Configuration"
|
||||
the same credential (passphrase or PMK).
|
||||
Disable to advertise an open (unencrypted) service.
|
||||
|
||||
config EXAMPLE_NAN_GROUP_DATA_PROT
|
||||
bool "Protect group-addressed datapath traffic (ND-GTK)"
|
||||
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||
default y
|
||||
help
|
||||
Negotiate and install an ND-GTK so group-addressed (multicast/
|
||||
broadcast) frames on the NAN datapath are encrypted. This is
|
||||
required for IPv6 over the secured datapath (Neighbor Discovery,
|
||||
MLD). Both peers must enable this for group keys to be exchanged.
|
||||
|
||||
choice EXAMPLE_NAN_SECURITY_METHOD
|
||||
prompt "Security Method"
|
||||
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||
|
||||
@@ -133,6 +133,9 @@ void wifi_nan_publish(void)
|
||||
};
|
||||
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
|
||||
wifi_nan_discovery_security_params_t security_cfg = {
|
||||
#ifdef CONFIG_EXAMPLE_NAN_GROUP_DATA_PROT
|
||||
.group_data_prot = 1, /* distribute/accept ND-GTK for group-addressed data */
|
||||
#endif
|
||||
.num_credentials = 1,
|
||||
.creds = {
|
||||
{
|
||||
|
||||
@@ -45,6 +45,16 @@ menu "Example Configuration"
|
||||
(passphrase or PMK) and sets up an encrypted NDP.
|
||||
Disable to discover open (unencrypted) services.
|
||||
|
||||
config EXAMPLE_NAN_GROUP_DATA_PROT
|
||||
bool "Protect group-addressed datapath traffic (ND-GTK)"
|
||||
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||
default y
|
||||
help
|
||||
Negotiate and install an ND-GTK so group-addressed (multicast/
|
||||
broadcast) frames on the NAN datapath are encrypted. This is
|
||||
required for IPv6 over the secured datapath (Neighbor Discovery,
|
||||
MLD). Both peers must enable this for group keys to be exchanged.
|
||||
|
||||
choice EXAMPLE_NAN_SECURITY_METHOD
|
||||
prompt "Security Method"
|
||||
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||
|
||||
@@ -227,6 +227,9 @@ void wifi_nan_subscribe(void)
|
||||
};
|
||||
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
|
||||
wifi_nan_discovery_security_params_t security_cfg = {
|
||||
#ifdef CONFIG_EXAMPLE_NAN_GROUP_DATA_PROT
|
||||
.group_data_prot = 1, /* distribute/accept ND-GTK for group-addressed data */
|
||||
#endif
|
||||
.num_credentials = 1,
|
||||
.creds = {
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user