Merge branch 'fix/backport_some_wifi_fixes_v6.1' into 'release/v6.1'

fix(wifi): backport some wifi fixes to v6.1

See merge request espressif/esp-idf!50183
This commit is contained in:
Jiang Jiang Jian
2026-07-02 17:38:57 +08:00
58 changed files with 1033 additions and 699 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ void * esp_coex_common_spin_lock_create_wrapper(void)
void *mux = heap_caps_malloc(sizeof(portMUX_TYPE), MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
if (mux) {
memcpy(mux,&tmp,sizeof(portMUX_TYPE));
memcpy(mux, &tmp, sizeof(portMUX_TYPE));
return mux;
}
return NULL;
@@ -39,7 +39,7 @@ bool IRAM_ATTR esp_coex_common_env_is_chip_wrapper(void)
void *esp_coex_common_spin_lock_create_wrapper(void)
{
portMUX_TYPE tmp = portMUX_INITIALIZER_UNLOCKED;
void *mux = malloc(sizeof(portMUX_TYPE));
void *mux = heap_caps_malloc(sizeof(portMUX_TYPE), MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
if (mux) {
memcpy(mux, &tmp, sizeof(portMUX_TYPE));
@@ -40,7 +40,7 @@ bool IRAM_ATTR esp_coex_common_env_is_chip_wrapper(void)
void *esp_coex_common_spin_lock_create_wrapper(void)
{
portMUX_TYPE tmp = portMUX_INITIALIZER_UNLOCKED;
void *mux = malloc(sizeof(portMUX_TYPE));
void *mux = heap_caps_malloc(sizeof(portMUX_TYPE), MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
if (mux) {
memcpy(mux, &tmp, sizeof(portMUX_TYPE));
@@ -40,7 +40,7 @@ bool IRAM_ATTR esp_coex_common_env_is_chip_wrapper(void)
void *esp_coex_common_spin_lock_create_wrapper(void)
{
portMUX_TYPE tmp = portMUX_INITIALIZER_UNLOCKED;
void *mux = malloc(sizeof(portMUX_TYPE));
void *mux = heap_caps_malloc(sizeof(portMUX_TYPE), MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
if (mux) {
memcpy(mux, &tmp, sizeof(portMUX_TYPE));
@@ -40,10 +40,10 @@ bool IRAM_ATTR esp_coex_common_env_is_chip_wrapper(void)
void *esp_coex_common_spin_lock_create_wrapper(void)
{
portMUX_TYPE tmp = portMUX_INITIALIZER_UNLOCKED;
void *mux = malloc(sizeof(portMUX_TYPE));
void *mux = heap_caps_malloc(sizeof(portMUX_TYPE), MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL);
if (mux) {
memcpy(mux, &tmp, sizeof(portMUX_TYPE));
memcpy(mux,&tmp,sizeof(portMUX_TYPE));
return mux;
}
return NULL;
@@ -2,5 +2,5 @@
components/esp_coex/test_apps/:
disable:
- if: IDF_TARGET not in ["esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32c6", "esp32h2", "esp32c5", "esp32c61", "esp32s31"]
reason: only supported with s2, s3, c3, c2, c6, h2, c5, c61 and s31
- if: IDF_TARGET not in ["esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32c6", "esp32h2", "esp32c5", "esp32c61", "esp32s31", "esp32h4", "esp32h21"]
reason: only supported with s2, s3, c3, c2, c6, h2, c5, c61, s31, esp32h4 and esp32h21
@@ -1,3 +1,3 @@
| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | --------- |
| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | --------- |
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -36,12 +36,15 @@ TEST_CASE("enable external coex", "[external_coex]")
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
#define EXTERNAL_COEX_CONF 0x600AF4A0
#define WDEV_RW_BT_COEX_EN (BIT(9))
#elif CONFIG_IDF_TARGET_ESP32H2
#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32H21
#define EXTERNAL_COEX_CONF 0x600AD4A0
#define WDEV_RW_BT_COEX_EN (BIT(9))
#elif CONFIG_IDF_TARGET_ESP32S31
#define EXTERNAL_COEX_CONF 0x2010F4A0
#define WDEV_RW_BT_COEX_EN (BIT(9))
#elif CONFIG_IDF_TARGET_ESP32H4
#define EXTERNAL_COEX_CONF 0x600CF4A0
#define WDEV_RW_BT_COEX_EN (BIT(9))
#endif
esp_extern_coex_work_mode_t mode = EXTERNAL_COEX_LEADER_ROLE;
@@ -8,9 +8,10 @@ from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@idf_parametrize(
'target',
['esp32h2', 'esp32c3', 'esp32s2', 'esp32s3', 'esp32c6', 'esp32c61', 'esp32c5', 'esp32s31'],
['esp32h2', 'esp32c3', 'esp32s2', 'esp32s3', 'esp32c6', 'esp32c61', 'esp32c5', 'esp32s31', 'esp32h4', 'esp32h21'],
indirect=['target'],
)
@pytest.mark.temp_skip_ci(targets=['esp32h21'], reason='lack of runners')
def test_external_coex_unit_test(dut: Dut) -> None:
dut.run_all_single_board_cases()
+1 -1
View File
@@ -525,7 +525,7 @@ pm_disable_sleep_delay_timer = 0x40001b74;
/*pm_dream = 0x40001b78;*/
pm_mac_wakeup = 0x40001b7c;
pm_mac_sleep = 0x40001b80;
pm_enable_active_timer = 0x40001b84;
//pm_enable_active_timer = 0x40001b84;
pm_enable_sleep_delay_timer = 0x40001b88;
pm_local_tsf_process = 0x40001b8c;
//pm_set_beacon_filter = 0x40001b90;
+1 -1
View File
@@ -712,7 +712,7 @@ pm_disable_sleep_delay_timer = 0x40001650;
/*pm_dream = 0x40001654;*/
pm_mac_wakeup = 0x40001658;
pm_mac_sleep = 0x4000165c;
pm_enable_active_timer = 0x40001660;
//pm_enable_active_timer = 0x40001660;
pm_enable_sleep_delay_timer = 0x40001664;
pm_local_tsf_process = 0x40001668;
//pm_set_beacon_filter = 0x4000166c;
@@ -110,7 +110,7 @@ pm_disable_sleep_delay_timer = 0x40000d50;
pm_dream = 0x40000d54;
pm_mac_wakeup = 0x40000d58;
pm_mac_sleep = 0x40000d5c;
pm_enable_active_timer = 0x40000d60;
//pm_enable_active_timer = 0x40000d60;
pm_enable_sleep_delay_timer = 0x40000d64;
pm_local_tsf_process = 0x40000d68;
//pm_set_beacon_filter = 0x40000d6c;
@@ -107,7 +107,7 @@ pm_disable_sleep_delay_timer = 0x40000cbc;
pm_dream = 0x40000cc0;
pm_mac_wakeup = 0x40000cc4;
pm_mac_sleep = 0x40000cc8;
pm_enable_active_timer = 0x40000ccc;
//pm_enable_active_timer = 0x40000ccc;
pm_enable_sleep_delay_timer = 0x40000cd0;
pm_local_tsf_process = 0x40000cd4;
/*pm_set_beacon_filter = 0x40000cd8;*/
+1 -1
View File
@@ -966,7 +966,7 @@ pm_disable_sleep_delay_timer = 0x40005430;
/*pm_dream = 0x4000543c;*/
pm_mac_wakeup = 0x40005448;
pm_mac_sleep = 0x40005454;
pm_enable_active_timer = 0x40005460;
//pm_enable_active_timer = 0x40005460;
pm_enable_sleep_delay_timer = 0x4000546c;
pm_local_tsf_process = 0x40005478;
//pm_set_beacon_filter = 0x40005484;
@@ -109,7 +109,7 @@ pm_disable_sleep_delay_timer = 0x2f800e6c;
pm_dream = 0x2f800e70;
pm_mac_wakeup = 0x2f800e74;
pm_mac_sleep = 0x2f800e78;
pm_enable_active_timer = 0x2f800e7c;
//pm_enable_active_timer = 0x2f800e7c;
pm_enable_sleep_delay_timer = 0x2f800e80;
pm_local_tsf_process = 0x2f800e84;
pm_set_beacon_filter = 0x2f800e88;
@@ -139,7 +139,7 @@ pm_extend_tbtt_adaptive_servo = 0x2f800ee4;
pm_scale_listen_interval = 0x2f800ee8;
pm_parse_mbssid_element = 0x2f800eec;
pm_disconnected_wake = 0x2f800ef0;
pm_tx_data_process = 0x2f800ef4;
//pm_tx_data_process = 0x2f800ef4;
pm_is_twt_awake = 0x2f800ef8;
pm_enable_twt_keep_alive = 0x2f800efc;
pm_twt_on_tsf_timer = 0x2f800f00;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -230,35 +230,35 @@
#elif CONFIG_IDF_TARGET_ESP32S31
//TODO: Dummy values, remove or update when FTM loopback and auto-calibration is verified
// 20 MHz FTM in 20MHz PHY - Initiator Values
#define EST_PHY_INIT_FTM_COMP_20_20U_MHZ 859 // Connected Initiator in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20U_MHZ_DIS 859 // Disconnected Initiator in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20D_MHZ 869 // Connected Initiator in 20MHz (Ch 11) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20D_MHZ_DIS 869 // Disconnected Initiator in 20MHz (Ch 11) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20U_MHZ 437 // Connected Initiator in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20U_MHZ_DIS 433 // Disconnected Initiator in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20D_MHZ 443 // Connected Initiator in 20MHz (Ch 11) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_20D_MHZ_DIS 442 // Disconnected Initiator in 20MHz (Ch 11) using 20MHz FTM
// 20 MHz FTM in 20MHz PHY - Responder Values
#define EST_PHY_RESP_FTM_COMP_20_20U_MHZ 867 // Connected Responder in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20U_MHZ_DIS 867 // Disconnected Responder in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20D_MHZ 857 // Connected Responder in 20MHz (Ch 11) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20D_MHZ_DIS 857 // Disconnected Responder in 20MHz (Ch 11) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20U_MHZ 426 // Connected Responder in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20U_MHZ_DIS 429 // Disconnected Responder in 20MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20D_MHZ 420 // Connected Responder in 20MHz (Ch 11) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_20D_MHZ_DIS 421 // Disconnected Responder in 20MHz (Ch 11) using 20MHz FTM
// 20 MHz FTM in 40MHz PHY - Initiator Values
#define EST_PHY_INIT_FTM_COMP_20_40U_MHZ 746 // Connected Initiator in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40U_MHZ_DIS 744 // Disconnected Initiator in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40D_MHZ 758 // Connected Initiator in 40MHz (Ch 11) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40D_MHZ_DIS 755 // Disconnected Initiator in 40MHz (Ch 11) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40U_MHZ 462 // Connected Initiator in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40U_MHZ_DIS 433 // Disconnected Initiator in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40D_MHZ 438 // Connected Initiator in 40MHz (Ch 11) using 20MHz FTM
#define EST_PHY_INIT_FTM_COMP_20_40D_MHZ_DIS 442 // Disconnected Initiator in 40MHz (Ch 11) using 20MHz FTM
// 20 MHz FTM in 40MHz PHY - Responder Values
#define EST_PHY_RESP_FTM_COMP_20_40U_MHZ 754 // Connected Responder in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40U_MHZ_DIS 753 // Disconnected Responder in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40D_MHZ 744 // Connected Responder in 40MHz (Ch 11) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40D_MHZ_DIS 744 // Disconnected Responder in 40MHz (Ch 11) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40U_MHZ 425 // Connected Responder in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40U_MHZ_DIS 430 // Disconnected Responder in 40MHz (Ch 1) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40D_MHZ 420 // Connected Responder in 40MHz (Ch 11) using 20MHz FTM
#define EST_PHY_RESP_FTM_COMP_20_40D_MHZ_DIS 420 // Disconnected Responder in 40MHz (Ch 11) using 20MHz FTM
// 40 MHz FTM in 40MHz PHY - Initiator Values
#define EST_PHY_INIT_FTM_COMP_40_40U_MHZ 931 // Connected Initiator in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40U_MHZ_DIS 931 // Disconnected Initiator in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40D_MHZ 931 // Connected Initiator in 40MHz (Ch 11) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40D_MHZ_DIS 931 // Disconnected Initiator in 40MHz (Ch 11) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40U_MHZ 237 // Connected Initiator in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40U_MHZ_DIS 237 // Disconnected Initiator in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40D_MHZ 234 // Connected Initiator in 40MHz (Ch 11) using 40MHz FTM
#define EST_PHY_INIT_FTM_COMP_40_40D_MHZ_DIS 234 // Disconnected Initiator in 40MHz (Ch 11) using 40MHz FTM
// 40 MHz FTM in 40MHz PHY - Responder Values
#define EST_PHY_RESP_FTM_COMP_40_40U_MHZ 566 // Connected Responder in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40U_MHZ_DIS 566 // Disconnected Responder in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40D_MHZ 567 // Connected Responder in 40MHz (Ch 11) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40D_MHZ_DIS 567 // Disconnected Responder in 40MHz (Ch 11) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40U_MHZ 626 // Connected Responder in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40U_MHZ_DIS 626 // Disconnected Responder in 40MHz (Ch 1) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40D_MHZ 629 // Connected Responder in 40MHz (Ch 11) using 40MHz FTM
#define EST_PHY_RESP_FTM_COMP_40_40D_MHZ_DIS 629 // Disconnected Responder in 40MHz (Ch 11) using 40MHz FTM
#endif
/********************************************** 5 GHz Values *******************************************************/
@@ -195,6 +195,7 @@ struct nan_sync_callbacks {
void (* receive_pasn)(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t status);
uint32_t (* get_nira_len)(void);
int (* construct_nira)(uint8_t *frm);
bool (*verify_nira)(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
};
/* Host helpers for NAN encrypted-datapath, registered via
@@ -231,6 +232,9 @@ struct nan_secure_dp_funcs {
* encrypted KDE payload (GTK/IGTK/BIGTK). */
int (*ndp_security_install_get_shared_desc_len)(void);
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);
/* --- CSIA / SCIA construction. Each writes a complete NAN
* attribute (header + body) at @c frm and returns bytes
* written, or -1 on error. --- */
@@ -1197,6 +1201,17 @@ uint32_t esp_nan_get_nira_len(void);
*/
int esp_nan_construct_nira(uint8_t *frm);
/**
* @brief Verify a received NAN Identity Resolution Attribute (NIRA)
*
* @param[in] peer_mac NMI of the sender
* @param[in] nira_attr NIRA attribute buffer
* @param[in] nira_attr_len Attribute length in bytes
*
* @return true if the tag matches, false otherwise
*/
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
/**
* @brief Get the time information from the MAC clock. The time is precise only if modem sleep or light sleep is not enabled.
*
@@ -78,26 +78,26 @@ typedef int (*esp_aes_128_encrypt_t)(const unsigned char *key, const unsigned ch
typedef int (*esp_aes_128_decrypt_t)(const unsigned char *key, const unsigned char *iv, unsigned char *data, int data_len);
/**
* @brief The AES wrap callback function used by esp_wifi.
* @brief The AES key wrap (RFC 3394) callback function used by esp_wifi.
*
* @param kek 16-octet Key encryption key (KEK).
* @param kek Key encryption key (KEK).
* @param kek_len Length of the KEK in bytes.
* @param n Length of the plaintext key in 64-bit units;
* @param plain Plaintext key to be wrapped, n * 64 bits
* @param cipher Wrapped key, (n + 1) * 64 bits
*
*/
typedef int (*esp_aes_wrap_t)(const unsigned char *kek, int n, const unsigned char *plain, unsigned char *cipher);
typedef int (*esp_aes_wrap_t)(const unsigned char *kek, size_t kek_len, int n, const unsigned char *plain, unsigned char *cipher);
/**
* @brief The AES unwrap callback function used by esp_wifi.
* @brief The AES key unwrap (RFC 3394) callback function used by esp_wifi.
*
* @param kek 16-octet Key decryption key (KEK).
* @param kek Key encryption key (KEK).
* @param kek_len Length of the KEK in bytes.
* @param n Length of the plaintext key in 64-bit units;
* @param cipher Wrapped key to be unwrapped, (n + 1) * 64 bits
* @param plain Plaintext key, n * 64 bits
*
*/
typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, int n, const unsigned char *cipher, unsigned char *plain);
typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, size_t kek_len, int n, const unsigned char *cipher, unsigned char *plain);
/**
* @brief The SHA256 callback function used by esp_wifi.
@@ -404,6 +404,8 @@ typedef struct wpa_crypto_funcs_t {
esp_ccmp_encrypt_t ccmp_encrypt; /**< Encrypt data callback function using CCMP */
esp_aes_gmac_t aes_gmac; /**< One-Key GMAC hash callback function with AES for MIC computation */
esp_sha256_vector_t sha256_vector; /**< SHA256 hash callback function for data vector */
esp_aes_wrap_t aes_wrap; /**< The AES key wrap (RFC 3394) callback function used by esp_wifi */
esp_aes_unwrap_t aes_unwrap; /**< The AES key unwrap (RFC 3394) callback function used by esp_wifi */
} wpa_crypto_funcs_t;
/**
@@ -604,9 +604,8 @@ typedef struct {
uint8_t scan_time; /**< Scan time in seconds while searching for a NAN cluster */
uint16_t warm_up_sec; /**< Warm up time before assuming NAN Anchor Master role */
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
uint8_t nik[ESP_WIFI_NAN_NIK_LEN]; /**< Optional NIK. Auto-generated when nik_valid is false. */
uint8_t nik_valid: 1; /**< NIK present in nik[] and should be used as-is. */
uint8_t reserved: 7; /**< Reserved for future use. */
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. */
} wifi_nan_sync_config_t;
/**
@@ -874,7 +873,9 @@ typedef struct {
#define ESP_WIFI_NDP_ROLE_RESPONDER 2 /**< Responder role for NAN Data Path */
#define ESP_WIFI_NAN_NDP_PMK_LEN 32 /**< Length of NAN Datapath PMK */
#define ESP_WIFI_NAN_NPK_LEN ESP_WIFI_NAN_NDP_PMK_LEN /**< Length of NAN Pairwise Key (same as NDP PMK) */
#define ESP_WIFI_NAN_NDP_PMKID_LEN 16 /**< Length of NAN Datapath PMKID */
#define ESP_WIFI_NAN_MAX_PEER_CREDS 2 /**< Maximum number of NAN peer credentials that can be stored */
#define ESP_WIFI_NAN_MAX_CREDS_PER_SVC 4 /**< Maximum number of NAN security credentials per service (passphrase/PMK entries) */
#define ESP_WIFI_MAX_SVC_NAME_LEN 256 /**< Maximum length of NAN service name */
@@ -1307,7 +1308,8 @@ typedef enum {
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN PASN pairwise key installation completed */
WIFI_EVENT_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 */
WIFI_EVENT_MAX, /**< Invalid Wi-Fi event ID */
} wifi_event_t;
@@ -1578,6 +1580,13 @@ typedef struct {
uint8_t ssi[]; /**< Service specific info of Subscriber */
} wifi_event_nan_replied_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_CLUSTER_JOIN event
*/
typedef struct {
uint8_t cluster_id[6]; /**< NAN Cluster ID (BSSID) that was joined/started by the device */
} wifi_event_nan_cluster_join_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_RECEIVE event
*/
@@ -1662,9 +1671,6 @@ typedef struct {
/**
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
*
* Posted when PASN pairwise key installation completes.
* Distinct from WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED (NPBA follow-up bootstrapping).
*/
typedef struct {
uint8_t status; /**< 0=Accepted, 1=Rejected (wifi_nan_pairing_status_t) */
@@ -78,26 +78,26 @@ typedef int (*esp_aes_128_encrypt_t)(const unsigned char *key, const unsigned ch
typedef int (*esp_aes_128_decrypt_t)(const unsigned char *key, const unsigned char *iv, unsigned char *data, int data_len);
/**
* @brief The AES wrap callback function used by esp_wifi.
* @brief The AES key wrap (RFC 3394) callback function used by esp_wifi.
*
* @param kek 16-octet Key encryption key (KEK).
* @param kek Key encryption key (KEK).
* @param kek_len Length of the KEK in bytes.
* @param n Length of the plaintext key in 64-bit units;
* @param plain Plaintext key to be wrapped, n * 64 bits
* @param cipher Wrapped key, (n + 1) * 64 bits
*
*/
typedef int (*esp_aes_wrap_t)(const unsigned char *kek, int n, const unsigned char *plain, unsigned char *cipher);
typedef int (*esp_aes_wrap_t)(const unsigned char *kek, size_t kek_len, int n, const unsigned char *plain, unsigned char *cipher);
/**
* @brief The AES unwrap callback function used by esp_wifi.
* @brief The AES key unwrap (RFC 3394) callback function used by esp_wifi.
*
* @param kek 16-octet Key decryption key (KEK).
* @param kek Key encryption key (KEK).
* @param kek_len Length of the KEK in bytes.
* @param n Length of the plaintext key in 64-bit units;
* @param cipher Wrapped key to be unwrapped, (n + 1) * 64 bits
* @param plain Plaintext key, n * 64 bits
*
*/
typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, int n, const unsigned char *cipher, unsigned char *plain);
typedef int (*esp_aes_unwrap_t)(const unsigned char *kek, size_t kek_len, int n, const unsigned char *cipher, unsigned char *plain);
/**
* @brief The SHA256 callback function used by esp_wifi.
@@ -404,6 +404,8 @@ typedef struct wpa_crypto_funcs_t {
esp_ccmp_encrypt_t ccmp_encrypt; /**< Encrypt data callback function using CCMP */
esp_aes_gmac_t aes_gmac; /**< One-Key GMAC hash callback function with AES for MIC computation */
esp_sha256_vector_t sha256_vector; /**< SHA256 hash callback function for data vector */
esp_aes_wrap_t aes_wrap; /**< The AES key wrap (RFC 3394) callback function used by esp_wifi */
esp_aes_unwrap_t aes_unwrap; /**< The AES key unwrap (RFC 3394) callback function used by esp_wifi */
} wpa_crypto_funcs_t;
/**
@@ -604,9 +604,8 @@ typedef struct {
uint8_t scan_time; /**< Scan time in seconds while searching for a NAN cluster */
uint16_t warm_up_sec; /**< Warm up time before assuming NAN Anchor Master role */
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
uint8_t nik[ESP_WIFI_NAN_NIK_LEN]; /**< Optional NIK. Auto-generated when nik_valid is false. */
uint8_t nik_valid: 1; /**< NIK present in nik[] and should be used as-is. */
uint8_t reserved: 7; /**< Reserved for future use. */
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. */
} wifi_nan_sync_config_t;
/**
@@ -874,7 +873,9 @@ typedef struct {
#define ESP_WIFI_NDP_ROLE_RESPONDER 2 /**< Responder role for NAN Data Path */
#define ESP_WIFI_NAN_NDP_PMK_LEN 32 /**< Length of NAN Datapath PMK */
#define ESP_WIFI_NAN_NPK_LEN ESP_WIFI_NAN_NDP_PMK_LEN /**< Length of NAN Pairwise Key (same as NDP PMK) */
#define ESP_WIFI_NAN_NDP_PMKID_LEN 16 /**< Length of NAN Datapath PMKID */
#define ESP_WIFI_NAN_MAX_PEER_CREDS 2 /**< Maximum number of NAN peer credentials that can be stored */
#define ESP_WIFI_NAN_MAX_CREDS_PER_SVC 4 /**< Maximum number of NAN security credentials per service (passphrase/PMK entries) */
#define ESP_WIFI_MAX_SVC_NAME_LEN 256 /**< Maximum length of NAN service name */
@@ -1307,7 +1308,8 @@ typedef enum {
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN PASN pairwise key installation completed */
WIFI_EVENT_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 */
WIFI_EVENT_MAX, /**< Invalid Wi-Fi event ID */
} wifi_event_t;
@@ -1578,6 +1580,13 @@ typedef struct {
uint8_t ssi[]; /**< Service specific info of Subscriber */
} wifi_event_nan_replied_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_CLUSTER_JOIN event
*/
typedef struct {
uint8_t cluster_id[6]; /**< NAN Cluster ID (BSSID) that was joined/started by the device */
} wifi_event_nan_cluster_join_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_RECEIVE event
*/
@@ -1662,9 +1671,6 @@ typedef struct {
/**
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
*
* Posted when PASN pairwise key installation completes.
* Distinct from WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED (NPBA follow-up bootstrapping).
*/
typedef struct {
uint8_t status; /**< 0=Accepted, 1=Rejected (wifi_nan_pairing_status_t) */
@@ -51,13 +51,141 @@ void esp_nan_action_stop(void);
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
#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. */
#ifndef NAN_PAIRING_PINCODE_MIN
#define NAN_PAIRING_PINCODE_MIN 0
#endif
#ifndef NAN_PAIRING_PINCODE_MAX
#define NAN_PAIRING_PINCODE_MAX 999999
#endif
union pairing_cred_t {
uint32_t pincode; /**< 6-digit PIN in range of NAN_PAIRING_PINCODE_MIN to NAN_PAIRING_PINCODE_MAX */
};
enum nan_pairing_role {
NAN_PAIRING_ROLE_INITIATOR,
NAN_PAIRING_ROLE_RESPONDER,
};
typedef struct {
uint8_t peer_svc_id;
uint8_t peer_nmi[6];
enum nan_pairing_role self_role;
union pairing_cred_t cred;
} wifi_nan_pairing_config_t;
/**
* @brief NAN Pairing Bootstrapping status values
*/
typedef enum {
WIFI_NAN_PAIRING_STATUS_ACCEPTED = 0, /**< Bootstrapping request accepted */
WIFI_NAN_PAIRING_STATUS_REJECTED = 1, /**< Bootstrapping request rejected */
WIFI_NAN_PAIRING_STATUS_COMEBACK = 2, /**< Comeback - responder needs more time */
} wifi_nan_pairing_status_t;
/**
* @brief NAN Pairing Bootstrapping Request parameters (initiator -> responder)
*
* Used by a subscriber (bootstrapping initiator) to send a bootstrapping
* request follow-up message with NPBA attribute (Type=Request) to a publisher.
*/
typedef struct {
uint8_t inst_id; /**< Own service instance id */
uint8_t peer_inst_id; /**< Peer's service instance id */
uint8_t peer_mac[6]; /**< Peer's NAN Management Interface MAC */
uint16_t selected_method; /**< One selected WIFI_NAN_BOOTSTRAP_* method bit */
wifi_nan_pairing_status_t status; /**< Set to COMEBACK when resending with cookie */
uint16_t comeback_after; /**< Comeback deferral time in TUs (only when status=COMEBACK) */
uint32_t cookie; /**< Opaque cookie from responder (only when status=COMEBACK) */
} wifi_nan_pairing_bootstrap_req_t;
/**
* @brief NAN Pairing Bootstrapping Response parameters (responder -> initiator)
*
* Used by a publisher (bootstrapping responder) to respond to a bootstrapping
* request follow-up message with NPBA attribute (Type=Response).
*/
typedef struct {
uint8_t inst_id; /**< Own service instance id */
uint8_t peer_inst_id; /**< Peer's service instance id */
uint8_t peer_mac[6]; /**< Peer's NAN Management Interface MAC */
wifi_nan_pairing_status_t status; /**< Accepted, Rejected, or Comeback */
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
uint8_t reason_code; /**< Rejection reason code (valid if rejected) */
uint16_t comeback_after; /**< Comeback deferral time in TUs (only when status=COMEBACK) */
uint32_t cookie; /**< Opaque cookie for comeback (only when status=COMEBACK) */
} wifi_nan_pairing_bootstrapping_resp_t;
/**
* @brief Send a NAN Pairing Bootstrapping request to a matched publisher
*
* @attention This API should be called by the Subscriber after a service match
* (WIFI_EVENT_NAN_SVC_MATCH) with a Publisher that has pairing enabled.
* The selected_method must match one of the methods advertised by the publisher.
*
* @param req Pairing bootstrapping request parameters.
*
* @return
* - ESP_OK: Bootstrapping request follow-up sent successfully
* - ESP_ERR_INVALID_ARG: Invalid parameters
* - ESP_FAIL: Failed to send
*/
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.
*
* @param resp Pairing bootstrapping response parameters.
*
* @return
* - ESP_OK: Bootstrapping response follow-up sent successfully
* - ESP_ERR_INVALID_ARG: Invalid parameters
* - ESP_FAIL: Failed to send
*/
esp_err_t esp_wifi_nan_bootstrap_response(wifi_nan_pairing_bootstrapping_resp_t *resp);
/**
* @brief Start NAN Pairing process after credentials are shared Out-of-band
*
* @attention This API should be called after Bootstrapping is completed
*
* @param req Pairing setup parameters.
*
* @return
* - ESP_OK: Bootstrapping request follow-up sent successfully
* - ESP_ERR_INVALID_ARG: Invalid parameters
* - ESP_FAIL: Failed to send
*/
esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg);
struct nan_pasn_data;
/**
* @brief Get the NAN PASN context held by the NAN App.
*
* @return Pointer to the current NAN PASN data, or NULL if none is set.
*/
struct nan_pasn_data *esp_nan_app_get_pasn_data(void);
/**
* @brief Store the NAN PASN context in the NAN App.
*
* @attention Ownership is not transferred; the caller remains responsible for
* the lifetime of @p pd. Pass NULL to clear the stored context.
*
* @param pd Pointer to the NAN PASN data to store, or NULL to clear it.
*/
void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd);
//void esp_nan_app_post_pasn_pairing_indication(const wifi_event_nan_pasn_pairing_indication_t *evt);
//void esp_nan_app_post_pasn_pairing_confirm(const wifi_event_nan_pasn_pairing_confirm_t *evt);
#endif /* CONFIG_ESP_WIFI_PASN_SUPPORT */
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
#ifdef __cplusplus
}
@@ -22,7 +22,8 @@ extern "C" {
.scan_time = 3, \
.warm_up_sec = 5, \
.disable_random_mac = false, \
.nik_valid = false, \
.reset_current_nvs_creds = false, \
.use_nvs_for_caching = false, \
};
#define NDP_STATUS_ACCEPTED 1
@@ -58,25 +59,6 @@ struct nan_peer_record {
uint8_t peer_ndi[6]; /**< Peer's NAN Data Interface address, only valid when ndp_id is non-zero */
};
#define NAN_PAIRING_PINCODE_MIN 000000
#define NAN_PAIRING_PINCODE_MAX 999999
union pairing_cred_t {
uint32_t pincode; /**< 6-digit PIN in range of NAN_PAIRING_PINCODE_MIN to NAN_PAIRING_PINCODE_MAX */
};
enum nan_pairing_role {
NAN_PAIRING_ROLE_INITIATOR,
NAN_PAIRING_ROLE_RESPONDER,
};
typedef struct {
uint8_t peer_svc_id;
uint8_t peer_nmi[6];
enum nan_pairing_role self_role;
union pairing_cred_t cred;
} wifi_nan_pairing_config_t;
/**
* @brief Start NAN Synchronization using the provided parameters.
* @note Discovery traffic begins only after publish/subscribe services are started.
@@ -197,97 +179,6 @@ esp_err_t esp_wifi_nan_get_peer_records(int *num_peer_records, uint8_t own_svc_i
esp_err_t esp_wifi_nan_get_peer_info(char *svc_name, uint8_t *peer_mac, struct nan_peer_record *peer_info);
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
/**
* @brief NAN Pairing Bootstrapping status values
*/
typedef enum {
WIFI_NAN_PAIRING_STATUS_ACCEPTED = 0, /**< Bootstrapping request accepted */
WIFI_NAN_PAIRING_STATUS_REJECTED = 1, /**< Bootstrapping request rejected */
WIFI_NAN_PAIRING_STATUS_COMEBACK = 2, /**< Comeback - responder needs more time */
} wifi_nan_pairing_status_t;
/**
* @brief NAN Pairing Bootstrapping Request parameters (initiator -> responder)
*
* Used by a subscriber (bootstrapping initiator) to send a bootstrapping
* request follow-up message with NPBA attribute (Type=Request) to a publisher.
*/
typedef struct {
uint8_t inst_id; /**< Own service instance id */
uint8_t peer_inst_id; /**< Peer's service instance id */
uint8_t peer_mac[6]; /**< Peer's NAN Management Interface MAC */
uint16_t selected_method; /**< One selected WIFI_NAN_BOOTSTRAP_* method bit */
wifi_nan_pairing_status_t status; /**< Set to COMEBACK when resending with cookie */
uint16_t comeback_after; /**< Comeback deferral time in TUs (only when status=COMEBACK) */
uint32_t cookie; /**< Opaque cookie from responder (only when status=COMEBACK) */
} wifi_nan_pairing_bootstrap_req_t;
/**
* @brief NAN Pairing Bootstrapping Response parameters (responder -> initiator)
*
* Used by a publisher (bootstrapping responder) to respond to a bootstrapping
* request follow-up message with NPBA attribute (Type=Response).
*/
typedef struct {
uint8_t inst_id; /**< Own service instance id */
uint8_t peer_inst_id; /**< Peer's service instance id */
uint8_t peer_mac[6]; /**< Peer's NAN Management Interface MAC */
wifi_nan_pairing_status_t status; /**< Accepted, Rejected, or Comeback */
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
uint8_t reason_code; /**< Rejection reason code (valid if rejected) */
uint16_t comeback_after; /**< Comeback deferral time in TUs (only when status=COMEBACK) */
uint32_t cookie; /**< Opaque cookie for comeback (only when status=COMEBACK) */
} wifi_nan_pairing_bootstrapping_resp_t;
/**
* @brief Send a NAN Pairing Bootstrapping request to a matched publisher
*
* @attention This API should be called by the Subscriber after a service match
* (WIFI_EVENT_NAN_SVC_MATCH) with a Publisher that has pairing enabled.
* The selected_method must match one of the methods advertised by the publisher.
*
* @param req Pairing bootstrapping request parameters.
*
* @return
* - ESP_OK: Bootstrapping request follow-up sent successfully
* - ESP_ERR_INVALID_ARG: Invalid parameters
* - ESP_FAIL: Failed to send
*/
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.
*
* @param resp Pairing bootstrapping response parameters.
*
* @return
* - ESP_OK: Bootstrapping response follow-up sent successfully
* - ESP_ERR_INVALID_ARG: Invalid parameters
* - ESP_FAIL: Failed to send
*/
esp_err_t esp_wifi_nan_bootstrap_response(wifi_nan_pairing_bootstrapping_resp_t *resp);
/**
* @brief Start NAN Pairing process after credentials are shared Out-of-band
*
* @attention This API should be called after Bootstrapping is completed
*
* @param req Pairing setup parameters.
*
* @return
* - ESP_OK: Bootstrapping request follow-up sent successfully
* - ESP_ERR_INVALID_ARG: Invalid parameters
* - ESP_FAIL: Failed to send
*/
esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg);
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) || defined(CONFIG_ESP_WIFI_NAN_USD_ENABLE)
@@ -24,30 +24,6 @@
#include "esp_private/esp_nan_usd.h"
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
bool esp_nan_ndp_info_present(void)
{
return true;
}
uint32_t esp_nan_ndp_get_info_len(void)
{
return 12;
}
int esp_nan_construct_ndp_info(uint8_t *frm)
{
static const uint8_t ndp_info_attr[] = {
0x01, 0x09, 0x00, 0x50, 0x6f, 0x9a, 0x02, 0x00, 0x02, 0x00, 0x05, 0x0d
};
if (!frm) {
return 0;
}
memcpy(frm, ndp_info_attr, sizeof(ndp_info_attr));
return sizeof(ndp_info_attr);
}
#if !CONFIG_ESP_WIFI_NAN_PAIRING
uint32_t esp_nan_get_nira_len(void)
{
@@ -59,6 +35,14 @@ int esp_nan_construct_nira(uint8_t *frm)
(void)frm;
return 0;
}
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len)
{
(void)peer_mac;
(void)nira_attr;
(void)nira_attr_len;
return false;
}
#endif
#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
@@ -406,6 +390,9 @@ static void nan_reset_service(uint8_t svc_id, bool reset_all)
while (idx < ESP_WIFI_NAN_MAX_SVC_SUPPORTED) {
p_own_svc = &s_nan_ctx.own_svc[idx++];
if (reset_all || (svc_id && p_own_svc->svc_id == svc_id)) {
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
nan_pairing_cancel_svc_pending(p_own_svc);
#endif
SLIST_FOREACH_SAFE(p_peer_svc, &(p_own_svc->peer_list), next, temp) {
SLIST_REMOVE(&(p_own_svc->peer_list), p_peer_svc, peer_svc_info, next);
os_free(p_peer_svc);
@@ -442,6 +429,36 @@ static bool nan_services_limit_reached(void)
return true;
}
/*
* Service ID = first 6 bytes of SHA256(lowercase(service_name))
* per Wi-Fi Aware v4.0 §5.1.5 (Service Name and Service ID).
*/
bool nan_compute_service_id(const char *service_name, uint8_t service_id[6])
{
if (!service_name || !g_wifi_default_wpa_crypto_funcs.sha256_vector) {
return false;
}
size_t name_len = strlen(service_name);
char *lower = os_malloc(name_len + 1);
if (!lower) {
return false;
}
strlcpy(lower, service_name, name_len + 1);
for (char *p = lower; *p; p++) {
*p = tolower((unsigned char) * p);
}
uint8_t hash[32];
const uint8_t *addr[1] = {(const uint8_t *)lower};
size_t len[1] = {name_len};
int ret = g_wifi_default_wpa_crypto_funcs.sha256_vector(1, addr, len, hash);
os_free(lower);
if (ret != 0) {
return false;
}
memcpy(service_id, hash, 6);
return true;
}
/* Pre-claim a slot for an upcoming publish/subscribe service. The slot is
* marked with a pending sentinel svc_id (0xFF) so nan_find_own_svc_by_name()
* can find it from the derive callback running on the WiFi task — that's how
@@ -451,7 +468,7 @@ static bool nan_services_limit_reached(void)
#define NAN_SVC_ID_PENDING 0xFF
static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_name[],
const wifi_nan_discovery_security_params_t *security_cfg)
const wifi_nan_discovery_security_params_t *security_cfg, wifi_nan_pairing_cfg_t *pairing)
{
struct own_svc_info *p_svc = NULL;
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
@@ -468,12 +485,18 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_
p_svc->type = type;
strlcpy(p_svc->svc_name, svc_name, ESP_WIFI_MAX_SVC_NAME_LEN);
SLIST_INIT(&p_svc->peer_list);
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
forced_memzero(&p_svc->user_cfg, sizeof(p_svc->user_cfg));
forced_memzero(&p_svc->derived_security, sizeof(p_svc->derived_security));
if (security_cfg) {
memcpy(&p_svc->user_cfg, security_cfg, sizeof(*security_cfg));
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (pairing) {
memcpy(&p_svc->pairing, pairing, sizeof(*pairing));
}
#endif
#else
(void)security_cfg;
#endif
@@ -483,13 +506,14 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_
/* Stamp the real svc_id and per-publish flags after the blob accepts the
* service. Looked up by name since the WiFi-task derive callback may have
* already populated derived_security[] before this runs. */
static void nan_finalize_own_svc(const char *svc_name, uint8_t id, bool ndp_resp_needed)
static void nan_finalize_own_svc(const char *svc_name, uint8_t id, bool ndp_resp_needed, uint8_t service_hash[6])
{
struct own_svc_info *p_svc = nan_find_own_svc_by_name(svc_name);
if (!p_svc) {
return;
}
p_svc->svc_id = id;
memcpy(p_svc->svc_hash, service_hash, 6);
if (p_svc->type == ESP_NAN_PUBLISH) {
p_svc->ndp_resp_needed = ndp_resp_needed;
}
@@ -726,8 +750,8 @@ void nan_app_post_event(int32_t event_id, void* event_data, size_t event_data_si
g_wifi_osi_funcs._event_post(WIFI_EVENT, event_id, event_data, event_data_size, OSI_FUNCS_TIME_BLOCKING);
}
void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *peer_info,
struct nan_cb_npba_t *npba)
static void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *peer_info,
struct nan_cb_npba_t *npba)
{
if (!peer_info) {
return;
@@ -838,7 +862,7 @@ void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *peer_info
os_free(evt);
}
void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_info)
static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_info)
{
if (!peer_info) {
return;
@@ -877,7 +901,7 @@ void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_info)
os_free(evt);
}
void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
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)
{
@@ -930,7 +954,7 @@ void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
os_free(evt);
}
void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *peer_info, uint32_t device_caps)
static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *peer_info, uint32_t device_caps)
{
/*
* Responder-side NDP indication. Security parsers (CSIA/SCIA/
@@ -1061,7 +1085,7 @@ void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *peer_inf
os_free(evt);
}
void nan_app_ndp_response_indication_cb(struct ndp_cb_peer_info *peer_info)
static void nan_app_ndp_response_indication_cb(struct ndp_cb_peer_info *peer_info)
{
if (!peer_info) {
return;
@@ -1111,7 +1135,7 @@ static void nan_ndp_confirm_teardown(const uint8_t peer_nmi[6], uint8_t ndp_id)
os_event_group_set_bits(nan_event_group, NDP_REJECTED);
}
void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer_info,
static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer_info,
uint8_t own_ndi[6], uint8_t ipv6_identifier[8])
{
if (!peer_info) {
@@ -1248,7 +1272,7 @@ done:
return;
}
void nan_app_ndp_terminated_cb(uint8_t reason, uint8_t ndp_id, uint8_t init_ndi[6])
static void nan_app_ndp_terminated_cb(uint8_t reason, uint8_t ndp_id, uint8_t init_ndi[6])
{
NAN_DATA_LOCK();
if (s_nan_ctx.nan_netif && !nan_is_datapath_active()) {
@@ -1274,7 +1298,7 @@ void nan_app_ndp_terminated_cb(uint8_t reason, uint8_t ndp_id, uint8_t init_ndi[
os_event_group_set_bits(nan_event_group, NDP_TERMINATED);
}
void nan_action_txdone_cb(uint32_t context, bool tx_status)
static void nan_action_txdone_cb(uint32_t context, bool tx_status)
{
if (nan_event_group && s_fup_context == context) {
if (tx_status) {
@@ -1285,7 +1309,7 @@ void nan_action_txdone_cb(uint32_t context, bool tx_status)
}
}
void esp_nan_ndp_tx_done_cb(uint8_t ndp_id, const uint8_t *peer_nmi, uint8_t msg_type, 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();
@@ -1327,6 +1351,8 @@ static struct nan_secure_dp_funcs s_nan_secure_dp_funcs = {
.get_scia_len = esp_nan_get_scia_len,
.get_shared_key_desc_attr_len = esp_nan_get_shared_key_desc_attr_len,
.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,
/* CSIA / SCIA construction */
.construct_csia = esp_nan_construct_csia,
@@ -1435,6 +1461,7 @@ void esp_nan_action_start(esp_netif_t *nan_netif)
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
.get_nira_len = esp_nan_get_nira_len,
.construct_nira = esp_nan_construct_nira,
.verify_nira = esp_nan_verify_nira,
.receive_pasn = handle_auth_pasn,
#endif
};
@@ -1505,17 +1532,36 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
return ESP_OK;
}
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
if (nan_cfg->nik_valid) {
memcpy(s_nan_ctx.own_nik, nan_cfg->nik, ESP_WIFI_NAN_NIK_LEN);
s_nan_ctx.own_nik_valid = true;
} else {
s_nan_ctx.own_nik_valid = false;
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;
if (nan_cfg->reset_current_nvs_creds) {
/* Start from a clean slate: drop every credential persisted in NVS. */
esp_wifi_nan_erase_all_creds();
} 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");
s_nan_ctx.own_nik_valid = false;
s_nan_ctx.num_peer_creds = 0;
}
if (!s_nan_ctx.own_nik_valid) {
if (os_get_random(s_nan_ctx.own_nik, ESP_WIFI_NAN_NIK_LEN) != 0) {
NAN_DATA_UNLOCK();
ESP_LOGE(TAG, "Failed to generate NAN NIK");
return ESP_FAIL;
}
s_nan_ctx.own_nik_valid = true;
/* 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);
}
}
/* Drop the cached NIRA tag; it was derived from the previous NIK. */
s_nan_ctx.nira_cached = false;
#endif
NAN_DATA_UNLOCK();
@@ -1588,9 +1634,23 @@ esp_err_t esp_wifi_nan_sync_stop(void)
}
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
static bool nan_check_paired_service_hash(uint8_t service_hash[6])
{
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
if (s_nan_ctx.peer_creds[i].is_valid &&
os_memcmp(s_nan_ctx.peer_creds[i].service_hash, service_hash, 6) == 0) {
return true;
}
}
return false;
}
#endif
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 "
@@ -1722,11 +1782,27 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
* into p_svc->derived_security[]. Doing the derive on WiFi task (not the
* app/main task) keeps PBKDF2's hardware-SHA polling off IDLE0 and avoids
* tripping the task watchdog when num_credentials > 1. */
if (!nan_compute_service_id(publish_cfg->service_name, service_id)) {
ESP_LOGE(TAG, "Failed to compute Service ID for %s", publish_cfg->service_name);
goto fail;
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (nan_check_paired_service_hash(service_id)) {
cfg->pairing->pairing_setup = false;
}
#endif
if (!nan_claim_own_svc_slot(ESP_NAN_PUBLISH, publish_cfg->service_name,
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
cfg->security_cfg
cfg->security_cfg,
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
cfg->pairing
#else
NULL
#endif
#else
NULL, NULL
#endif
)) {
ESP_LOGE(TAG, "No free service slot");
@@ -1740,7 +1816,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
}
ESP_LOGI(TAG, "Started Publishing %s [Service ID - %u]", publish_cfg->service_name, pub_id);
nan_finalize_own_svc(publish_cfg->service_name, pub_id, publish_cfg->ndp_resp_needed);
nan_finalize_own_svc(publish_cfg->service_name, pub_id, publish_cfg->ndp_resp_needed, service_id);
if (cfg->pairing) {
os_free(cfg->pairing);
}
@@ -1764,6 +1840,7 @@ 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 "
@@ -1864,8 +1941,20 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
/* 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);
goto fail;
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (nan_check_paired_service_hash(service_id)) {
subscribe_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->security_cfg, subscribe_cfg->pairing)) {
ESP_LOGE(TAG, "No free service slot");
goto fail;
}
@@ -1877,7 +1966,7 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
}
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);
nan_finalize_own_svc(subscribe_cfg->service_name, (uint8_t) sub_id, false, service_id);
NAN_DATA_UNLOCK();
return sub_id;
@@ -17,6 +17,8 @@
#include "esp_wifi_types_generic.h"
#include "esp_private/wifi.h"
#include "esp_nan.h"
#include "utils/common.h" /* u8/u16 typedefs required by esp_wifi_driver.h */
#include "esp_wifi_driver.h" /* wifi_nan_peer_creds_t + NAN credential NVS APIs */
#include "os.h"
#ifdef __cplusplus
@@ -220,9 +222,17 @@ struct own_svc_info {
* per-service array the blob caches in svc_entry->self_security_params[].
* Valid entries: [0, user_cfg.num_credentials). */
wifi_nan_security_params_t derived_security[ESP_WIFI_NAN_MAX_CREDS_PER_SVC];
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
wifi_nan_pairing_cfg_t pairing;
#endif
#endif
uint8_t num_peer_records;
SLIST_HEAD(peer_list_t, peer_svc_info) peer_list;
#if CONFIG_ESP_WIFI_NAN_PAIRING
bool nik_fup_pending;
uint8_t nik_fup_pending_peer_nmi[MACADDR_LEN];
#endif
uint8_t svc_hash[6];
};
/* Per-NDP link state */
@@ -287,9 +297,17 @@ typedef struct {
struct own_svc_info own_svc[ESP_WIFI_NAN_MAX_SVC_SUPPORTED];
esp_netif_t *nan_netif;
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/* Own NAN Identity Key (NIK) cached for pairing/security flows. */
uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN];
bool own_nik_valid;
uint8_t cached_nira_nonce[8];
uint8_t cached_nira_tag[8];
bool nira_cached;
/* Peer NIK/NPK credentials loaded from NVS at start and refreshed as peers
* are paired. Drives NIRA identity resolution; optionally persisted to NVS
* when @c use_nvs_for_caching is set. */
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS];
uint8_t num_peer_creds;
bool use_nvs_for_caching;
#endif
#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT
struct nan_pasn_data *nan_pasn_data;
@@ -309,36 +327,7 @@ struct own_svc_info *nan_find_own_svc(uint8_t svc_id);
struct own_svc_info *nan_find_own_svc_by_name(const char *svc_name);
struct ndl_info *nan_find_ndl(uint8_t ndp_id, uint8_t peer_nmi[]);
struct ndl_info *nan_find_ndl_by_pub_id_and_peer(uint8_t pub_id, const uint8_t *peer_nmi);
/* === nan_secure_dp_funcs initializer targets === */
/* Always-present (defined in nan_app.c) */
void esp_nan_ndp_tx_done_cb(uint8_t ndp_id, const uint8_t *peer_nmi,
uint8_t msg_type, bool tx_status);
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
void nan_app_post_event(int32_t event_id, void *event_data, size_t event_data_size);
struct peer_svc_info *nan_find_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[]);
EventGroupHandle_t nan_pairing_get_event_group(void);
uint32_t *nan_pairing_get_fup_context(void);
const uint8_t *nan_pairing_get_null_mac(void);
bool nan_pairing_validate_publish_bootstrapping(uint16_t bootstrapping_methods);
bool nan_pairing_validate_subscribe_bootstrapping(uint16_t bootstrapping_methods);
uint16_t nan_app_parse_npba_from_publish(const struct nan_cb_npba_t *npba);
void nan_app_bootstrap_indication(uint8_t peer_svc_id, uint8_t pub_id,
uint8_t peer_nmi[6], uint16_t selected_method);
void nan_app_bootstrap_completed(uint8_t status, uint8_t peer_svc_id, uint8_t sub_id,
uint8_t peer_nmi[6], uint16_t matched_method,
uint8_t reason_code);
bool nan_app_parse_npba_from_receive(uint8_t own_svc_id, uint8_t peer_svc_id,
uint8_t peer_nmi[6], const struct nan_cb_npba_t *npba);
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]);
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/* Security-gated (defined in nan_security.c) */
@@ -346,6 +335,8 @@ uint32_t esp_nan_get_csia_len(uint16_t own_csid_bitmap, uint16_t peer_csid_bitma
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);
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);
int esp_nan_construct_csia(uint8_t *frm, uint8_t pub_id,
uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
@@ -443,13 +434,33 @@ bool nan_security_service_match(const struct own_svc_info *own_svc,
const wifi_nan_peer_sdf_security_t *peer_sec);
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
#if CONFIG_ESP_WIFI_NAN_PAIRING
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
void nan_app_post_event(int32_t event_id, void *event_data, size_t event_data_size);
struct peer_svc_info *nan_find_peer_svc(uint8_t own_svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[]);
EventGroupHandle_t nan_pairing_get_event_group(void);
uint32_t *nan_pairing_get_fup_context(void);
const uint8_t *nan_pairing_get_null_mac(void);
bool nan_pairing_validate_publish_bootstrapping(uint16_t bootstrapping_methods);
bool nan_pairing_validate_subscribe_bootstrapping(uint16_t bootstrapping_methods);
uint16_t nan_app_parse_npba_from_publish(const struct nan_cb_npba_t *npba);
void nan_app_bootstrap_indication(uint8_t peer_svc_id, uint8_t pub_id,
uint8_t peer_nmi[6], uint16_t selected_method);
void nan_app_bootstrap_completed(uint8_t status, uint8_t peer_svc_id, uint8_t sub_id,
uint8_t peer_nmi[6], uint16_t matched_method,
uint8_t reason_code);
bool nan_app_parse_npba_from_receive(uint8_t own_svc_id, uint8_t peer_svc_id,
uint8_t peer_nmi[6], const struct nan_cb_npba_t *npba);
void nan_pairing_cancel_svc_pending(struct own_svc_info *own);
void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
const uint8_t *peer_mac,
const uint8_t *shared_key_attr,
size_t shared_key_attr_buf_len);
#endif
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/*
* Paired-peer cache API. Called from nan_pairing.c after the PASN install
* callback fires; consumed by the NDP security layer to source ND-PMK + cipher
@@ -468,7 +479,8 @@ esp_err_t nan_app_register_paired_peer(const uint8_t *peer_nmi,
const struct nan_paired_peer *nan_app_find_paired_peer(const uint8_t *peer_nmi);
void nan_app_remove_paired_peer(const uint8_t *peer_nmi);
void nan_app_clear_paired_peers(void);
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
#ifdef __cplusplus
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -21,11 +21,10 @@
#include "nan_i.h"
#include "os.h"
#include "utils/common.h"
#include "utils/eloop.h"
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
#include "esp_private/esp_supp_nan.h"
#include "apps_private/wifi_apps_private.h"
#endif
static const char *TAG = "nan_pairing";
@@ -34,7 +33,6 @@ static const char *TAG = "nan_pairing";
* Shared Key Descriptor (Wi-Fi Aware v4.0 §7.6.4.2). */
#define NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC 86400U
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
struct nan_pasn_data *esp_nan_app_get_pasn_data(void)
{
return s_nan_ctx.nan_pasn_data;
@@ -49,43 +47,8 @@ static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi,
uint8_t role,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len)
{
wifi_event_nan_pairing_complete_t evt = {0};
if (!peer_nmi) {
return;
}
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
/* Cache ND-PMK for future paired NDPs (Wi-Fi Aware v4.0 §7.6.4.2). The
* NDP cipher (CSID) is determined by the PASN cipher and resolved on the
* supplicant side before this callback fires; if either is missing, the
* pairing event still fires but the security layer will fall back to its
* service-credential path for any subsequent NDP. */
if (ndp_csid && nd_pmk && nd_pmk_len == ESP_WIFI_NAN_NDP_PMK_LEN) {
(void)nan_app_register_paired_peer(peer_nmi, role, ndp_csid,
nd_pmk, nd_pmk_len,
NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC);
} else {
ESP_LOGW(TAG, "Pairing complete for " MACSTR
": ND-PMK unavailable (csid=%u nd_pmk_len=%u); "
"paired-peer cache not updated",
MAC2STR(peer_nmi), ndp_csid, (unsigned)nd_pmk_len);
}
#else
(void)role;
(void)ndp_csid;
(void)nd_pmk;
(void)nd_pmk_len;
#endif
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
evt.reason_code = 0;
MACADDR_COPY(evt.peer_nmi, peer_nmi);
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
}
#endif
size_t nd_pmk_len,
uint32_t nik_lifetime_sec);
bool nan_pairing_validate_publish_bootstrapping(uint16_t bootstrapping_methods)
{
@@ -287,12 +250,19 @@ esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg)
return ESP_ERR_INVALID_ARG;
}
#if defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
if (cfg->cred.pincode != UINT32_MAX &&
cfg->cred.pincode > NAN_PAIRING_PINCODE_MAX) {
ESP_LOGE(TAG, "Invalid pincode %u (valid range %u..%u or UINT32_MAX for default)",
cfg->cred.pincode, NAN_PAIRING_PINCODE_MIN, NAN_PAIRING_PINCODE_MAX);
return ESP_ERR_INVALID_ARG;
}
int ret;
switch (cfg->self_role) {
case NAN_PAIRING_ROLE_RESPONDER:
ret = esp_nan_supp_pasn_responder_init(cfg->peer_nmi, cfg->cred.pincode,
NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC,
nan_pairing_key_installed_cb);
if (ret != 0) {
ESP_LOGE(TAG, "NAN PASN responder init failed for "MACSTR, MAC2STR(cfg->peer_nmi));
@@ -301,6 +271,7 @@ esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg)
break;
case NAN_PAIRING_ROLE_INITIATOR:
ret = esp_nan_supp_pasn_initiator_auth(cfg->peer_nmi, cfg->cred.pincode,
NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC,
nan_pairing_key_installed_cb);
if (ret != 0) {
ESP_LOGE(TAG, "NAN PASN initiator auth failed for "MACSTR, MAC2STR(cfg->peer_nmi));
@@ -312,10 +283,6 @@ esp_err_t esp_wifi_nan_pairing_start(wifi_nan_pairing_config_t *cfg)
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
#else
ESP_LOGE(TAG, "NAN PASN support not enabled");
return ESP_ERR_NOT_SUPPORTED;
#endif
}
/* NIRA: ID(1) + Len(2) + CipherVersion(1) + Nonce(8) + Tag(8) = 20 */
@@ -332,195 +299,6 @@ uint32_t esp_nan_get_nira_len(void)
return NAN_NIRA_ATTR_LEN;
}
int esp_nan_construct_nira(uint8_t *frm)
{
uint8_t nonce[NAN_NIRA_NONCE_LEN];
uint8_t tag[NAN_NIRA_TAG_LEN];
if (!frm) {
return 0;
}
if (os_get_random(nonce, sizeof(nonce)) != 0) {
ESP_LOGE(TAG, "NIRA: failed to generate nonce");
return 0;
}
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
uint8_t own_nmi[MACADDR_LEN];
const unsigned char *addr[3];
int len_arr[3];
uint8_t digest[32];
if (!s_nan_ctx.own_nik_valid) {
ESP_LOGW(TAG, "NIRA: own NIK is not available");
return 0;
}
if (!g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector) {
ESP_LOGE(TAG, "NIRA: hmac_sha256_vector not registered");
return 0;
}
if (esp_wifi_get_mac(WIFI_IF_NAN, own_nmi) != ESP_OK) {
ESP_LOGE(TAG, "NIRA: failed to read NAN NMI");
return 0;
}
/* Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI || Nonce)) */
addr[0] = (const unsigned char *)NAN_NIRA_STR;
len_arr[0] = NAN_NIRA_STR_LEN;
addr[1] = own_nmi;
len_arr[1] = MACADDR_LEN;
addr[2] = nonce;
len_arr[2] = NAN_NIRA_NONCE_LEN;
if (g_wifi_default_wpa_crypto_funcs.hmac_sha256_vector(s_nan_ctx.own_nik,
ESP_WIFI_NAN_NIK_LEN,
3, addr, len_arr,
digest) != 0) {
ESP_LOGE(TAG, "NIRA: tag derivation failed");
return 0;
}
memcpy(tag, digest, NAN_NIRA_TAG_LEN);
memset(digest, 0, sizeof(digest));
#else
/* NIRA requires an available NIK; skip when NAN security is not enabled. */
return 0;
#endif
uint8_t *p = frm;
*p++ = NAN_ATTR_ID_IDENTITY_RESOLUTION;
/* Attribute Length: CipherVersion(1) + Nonce(8) + Tag(8) = 17 */
*p++ = 17 & 0xFF;
*p++ = (17 >> 8) & 0xFF;
*p++ = NAN_NIRA_CIPHER_VER;
memcpy(p, nonce, NAN_NIRA_NONCE_LEN);
p += NAN_NIRA_NONCE_LEN;
memcpy(p, tag, NAN_NIRA_TAG_LEN);
p += NAN_NIRA_TAG_LEN;
return (int)(p - frm);
}
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT) && defined(CONFIG_ESP_WIFI_NAN_SECURITY)
#include "crypto/sha256.h"
#include "utils/eloop.h"
#include "crypto/aes_wrap.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
#define NAN_PAIRING_FUP_MIN_ATTR_LEN 3
#define NAN_PASN_KDE_OUI_TYPE_NIK 36
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
#define NAN_PASN_KEY_LIFETIME_NIK_BIT BIT(3)
/* NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC is defined at file scope above. */
#define NAN_ATTR_ID_SHARED_KEY_DESC 0x24
#define GSP_SUBATTR_TRANSPORT_PORT 0x00
#define GSP_SUBATTR_INSTANCE_NAME 0x03
#define NAN_PAIRING_SRV_PORT 3333
#define NAN_PAIRING_SRV_HOSTNAME "ESP-SRV-1234"
#define NAN_PAIRING_SSI_BUF_LEN 64
struct nan_pairing_fup_ctx {
uint8_t svc_id;
uint8_t peer_svc_id;
uint8_t peer_mac[MACADDR_LEN];
size_t shared_key_attr_len;
uint8_t shared_key_attr[];
};
static struct peer_svc_info *nan_find_peer_svc_exact(uint8_t own_svc_id, uint8_t peer_svc_id,
const uint8_t *peer_mac)
{
struct peer_svc_info *temp;
if (!own_svc_id || !peer_svc_id || !peer_mac) {
return NULL;
}
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
struct own_svc_info *own = &s_nan_ctx.own_svc[i];
if (own->svc_id != own_svc_id) {
continue;
}
SLIST_FOREACH(temp, &(own->peer_list), next) {
if (temp->svc_id == peer_svc_id &&
memcmp(temp->peer_nmi, peer_mac, MACADDR_LEN) == 0) {
return temp;
}
}
break;
}
return NULL;
}
/**
* Build the WFA OUI + GSP protocol header that prefixes a Generic Service
* Protocol SSI (Wi-Fi Aware v4.0 §4.2.7). Ported from esp-nsd
* @c add_ssi_gsp_attr (wa_sd.c).
*/
static int nan_pairing_add_ssi_gsp_attr(uint8_t *buf)
{
wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)buf;
static const uint8_t oui_wfa[WIFI_OUI_LEN] = { 0x50, 0x6F, 0x9A };
memcpy(wfa_ssi->wfa_oui, oui_wfa, WIFI_OUI_LEN);
wfa_ssi->proto = WIFI_SVC_PROTO_GENERIC;
return sizeof(wifi_nan_wfa_ssi_t);
}
/**
* Append a single GSP sub-attribute (TLV: ID(1) | Length(2 LE) | Value).
* Ported from esp-nsd @c add_gsp_subattr (wa_sd.c).
*/
static int nan_pairing_add_gsp_subattr(uint8_t *buf, uint8_t sub_attr_id,
const void *payload, uint16_t len)
{
uint8_t *p = buf;
*p++ = sub_attr_id;
*((uint16_t *)p) = len;
p += sizeof(uint16_t);
memcpy(p, payload, len);
return 1 + 2 + len;
}
/**
* Build a GSP SSI carrying the SRV record (Transport Port + Instance Name).
* Ported from esp-nsd @c get_ssi_for_srv_record (wa_sd.c) with a stack
* buffer and a fixed (hostname, port) instead of dynamic allocation.
*/
static size_t nan_pairing_build_srv_ssi(uint8_t *buf, size_t buf_len,
const char *hostname, uint16_t port)
{
uint8_t *p = buf;
size_t hostname_len;
size_t need;
if (!buf || !hostname) {
return 0;
}
hostname_len = strlen(hostname);
need = sizeof(wifi_nan_wfa_ssi_t)
+ (1 + 2 + sizeof(port))
+ (1 + 2 + hostname_len);
if (buf_len < need) {
return 0;
}
p += nan_pairing_add_ssi_gsp_attr(p);
p += nan_pairing_add_gsp_subattr(p, GSP_SUBATTR_TRANSPORT_PORT,
&port, sizeof(port));
p += nan_pairing_add_gsp_subattr(p, GSP_SUBATTR_INSTANCE_NAME,
hostname, hostname_len);
return (size_t)(p - buf);
}
/**
* Derive a NIRA tag for cipher version 0 (Wi-Fi Aware v4.0):
* Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI || Nonce))
@@ -562,38 +340,191 @@ static int nan_pairing_derive_nira_tag(const uint8_t nik[NAN_PASN_NIK_LEN],
return 0;
}
/**
* Build a NIRA attribute (ID 0x2B) into @a buf using the NIK and our NMI.
* Returns total attribute length on success, 0 on failure.
*/
static size_t nan_pairing_build_nira_attr(uint8_t *buf, size_t buf_len,
const uint8_t nik[NAN_PASN_NIK_LEN],
const uint8_t nmi_addr[ETH_ALEN])
int esp_nan_construct_nira(uint8_t *frm)
{
uint8_t nonce[NAN_NIRA_NONCE_LEN];
uint8_t tag[NAN_NIRA_TAG_LEN];
uint8_t *p = buf;
const uint8_t *nonce;
const uint8_t *tag;
uint8_t fresh_nonce[NAN_NIRA_NONCE_LEN];
uint8_t fresh_tag[NAN_NIRA_TAG_LEN];
if (!buf || !nik || !nmi_addr || buf_len < NAN_NIRA_ATTR_LEN) {
return 0;
}
if (os_get_random(nonce, sizeof(nonce)) != 0) {
return 0;
}
if (nan_pairing_derive_nira_tag(nik, nmi_addr, nonce, tag) != 0) {
if (!frm) {
return 0;
}
if (s_nan_ctx.nira_cached) {
nonce = s_nan_ctx.cached_nira_nonce;
tag = s_nan_ctx.cached_nira_tag;
} else {
uint8_t own_nmi[MACADDR_LEN];
if (!s_nan_ctx.own_nik_valid) {
ESP_LOGW(TAG, "NIRA: own NIK is not available");
return 0;
}
if (esp_wifi_get_mac(WIFI_IF_NAN, own_nmi) != ESP_OK) {
ESP_LOGE(TAG, "NIRA: failed to read NAN NMI");
return 0;
}
if (os_get_random(fresh_nonce, sizeof(fresh_nonce)) != 0) {
ESP_LOGE(TAG, "NIRA: failed to generate nonce");
return 0;
}
/* Tag = Truncate-64(HMAC-SHA-256(NIK, "NIR" || NMI || Nonce)) */
if (nan_pairing_derive_nira_tag(s_nan_ctx.own_nik, own_nmi,
fresh_nonce, fresh_tag) != 0) {
ESP_LOGE(TAG, "NIRA: tag derivation failed");
return 0;
}
memcpy(s_nan_ctx.cached_nira_nonce, fresh_nonce, NAN_NIRA_NONCE_LEN);
memcpy(s_nan_ctx.cached_nira_tag, fresh_tag, NAN_NIRA_TAG_LEN);
s_nan_ctx.nira_cached = true;
nonce = fresh_nonce;
tag = fresh_tag;
}
uint8_t *p = frm;
*p++ = NAN_ATTR_ID_IDENTITY_RESOLUTION;
/* Attribute Length (LE16): CipherVersion(1) + Nonce(8) + Tag(8) = 17 */
WPA_PUT_LE16(p, 1 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN);
p += 2;
/* Attribute Length: CipherVersion(1) + Nonce(8) + Tag(8) = 17 */
*p++ = 17 & 0xFF;
*p++ = (17 >> 8) & 0xFF;
*p++ = NAN_NIRA_CIPHER_VER;
memcpy(p, nonce, NAN_NIRA_NONCE_LEN);
p += NAN_NIRA_NONCE_LEN;
memcpy(p, tag, NAN_NIRA_TAG_LEN);
p += NAN_NIRA_TAG_LEN;
return (int)(p - frm);
}
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
#define NAN_PAIRING_FUP_MIN_ATTR_LEN 3
#define NAN_PASN_KDE_OUI_TYPE_NIK 36
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
#define NAN_PASN_KEY_LIFETIME_NIK_BIT BIT(3)
/* NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC is defined at file scope above. */
#define NAN_ATTR_ID_SHARED_KEY_DESC 0x24
#define NAN_PAIRING_NIK_FUP_TIMEOUT_SEC 2
struct nan_pairing_fup_ctx {
uint8_t svc_id;
uint8_t peer_svc_id;
uint8_t peer_mac[MACADDR_LEN];
size_t shared_key_attr_len;
uint8_t shared_key_attr[];
};
static struct peer_svc_info *nan_find_peer_svc_exact(uint8_t own_svc_id, uint8_t peer_svc_id,
const uint8_t *peer_mac)
{
struct peer_svc_info *temp;
if (!own_svc_id || !peer_svc_id || !peer_mac) {
return NULL;
}
for (int i = 0; i < ESP_WIFI_NAN_MAX_SVC_SUPPORTED; i++) {
struct own_svc_info *own = &s_nan_ctx.own_svc[i];
if (own->svc_id != own_svc_id) {
continue;
}
SLIST_FOREACH(temp, &(own->peer_list), next) {
if (temp->svc_id == peer_svc_id &&
memcmp(temp->peer_nmi, peer_mac, MACADDR_LEN) == 0) {
return temp;
}
}
break;
}
return NULL;
}
static void nan_pairing_nik_fup_timeout_cb(void *eloop_data, void *user_ctx)
{
struct own_svc_info *own = user_ctx;
wifi_event_nan_pairing_complete_t evt = {0};
(void)eloop_data;
if (!own || !own->nik_fup_pending) {
return;
}
own->nik_fup_pending = false;
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
evt.reason_code = WIFI_NAN_PAIRING_REASON_NIK_FUP_TIMEOUT;
MACADDR_COPY(evt.peer_nmi, own->nik_fup_pending_peer_nmi);
nan_app_remove_paired_peer(own->nik_fup_pending_peer_nmi);
struct peer_svc_info *peer = nan_find_peer_svc(own->svc_id, 0,
own->nik_fup_pending_peer_nmi);
esp_nan_complete_pairing(own->svc_id, peer ? peer->svc_id : 0);
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
ESP_LOGW(TAG, "Pairing succeeded but NIK caching timed out for peer " MACSTR
" (reason=%u)", MAC2STR(own->nik_fup_pending_peer_nmi), evt.reason_code);
}
void nan_pairing_cancel_svc_pending(struct own_svc_info *own)
{
if (!own || !own->nik_fup_pending) {
return;
}
eloop_cancel_timeout(nan_pairing_nik_fup_timeout_cb, NULL, own);
own->nik_fup_pending = false;
}
static void nan_pairing_arm_pending(struct own_svc_info *own, const uint8_t *peer_mac)
{
if (!own || !peer_mac) {
return;
}
nan_pairing_cancel_svc_pending(own);
MACADDR_COPY(own->nik_fup_pending_peer_nmi, peer_mac);
own->nik_fup_pending = true;
if (eloop_register_timeout(NAN_PAIRING_NIK_FUP_TIMEOUT_SEC, 0,
nan_pairing_nik_fup_timeout_cb, NULL,
own) != 0) {
own->nik_fup_pending = false;
}
}
/**
* Build the WFA OUI + GSP protocol header that prefixes a Generic Service
* Protocol SSI (Wi-Fi Aware v4.0 §4.2.7). Ported from esp-nsd
* @c add_ssi_gsp_attr (wa_sd.c).
*/
static int nan_pairing_add_ssi_gsp_attr(uint8_t *buf)
{
wifi_nan_wfa_ssi_t *wfa_ssi = (wifi_nan_wfa_ssi_t *)buf;
static const uint8_t oui_wfa[WIFI_OUI_LEN] = { 0x50, 0x6F, 0x9A };
memcpy(wfa_ssi->wfa_oui, oui_wfa, WIFI_OUI_LEN);
wfa_ssi->proto = WIFI_SVC_PROTO_GENERIC;
return sizeof(wifi_nan_wfa_ssi_t);
}
/**
* Build a GSP SSI (WFA OUI + proto=Generic header only).
*/
static size_t nan_pairing_build_srv_ssi(uint8_t *buf, size_t buf_len)
{
uint8_t *p = buf;
if (!buf || buf_len < sizeof(wifi_nan_wfa_ssi_t)) {
return 0;
}
p += nan_pairing_add_ssi_gsp_attr(p);
return (size_t)(p - buf);
}
@@ -663,8 +594,7 @@ static esp_err_t nan_app_send_pairing_followup(uint8_t svc_id, uint8_t peer_svc_
uint8_t key_desc[sizeof(struct wpa_eapol_key)] = {0};
uint8_t shared_key_wrapped[sizeof(struct wpa_eapol_key) + sizeof(wrapped)] = {0};
uint8_t nira_attr[NAN_NIRA_ATTR_LEN] = {0};
uint8_t srv_ssi[NAN_PAIRING_SSI_BUF_LEN] = {0};
uint8_t our_nmi[ETH_ALEN] = {0};
wifi_nan_wfa_ssi_t srv_ssi = {0};
uint8_t nik[NAN_PASN_NIK_LEN];
size_t plain_len;
size_t wrapped_len;
@@ -680,9 +610,14 @@ static esp_err_t nan_app_send_pairing_followup(uint8_t svc_id, uint8_t peer_svc_
(void)shared_key_attr;
(void)shared_key_attr_len;
if (!peer_mac || os_get_random(nik, sizeof(nik)) != 0) {
if (!peer_mac) {
return ESP_ERR_INVALID_ARG;
}
if (!s_nan_ctx.own_nik_valid) {
ESP_LOGW(TAG, "Pairing follow-up: own NIK is not available");
return ESP_ERR_INVALID_STATE;
}
memcpy(nik, s_nan_ctx.own_nik, sizeof(nik));
saved = nan_pasn_get_saved_keys();
if (!saved || !saved->kek_len) {
@@ -699,7 +634,11 @@ static esp_err_t nan_app_send_pairing_followup(uint8_t svc_id, uint8_t peer_svc_
return ESP_ERR_INVALID_SIZE;
}
wrapped_len = plain_len + 8;
if (aes_wrap(saved->kek, saved->kek_len, plain_len / 8, plain, wrapped) != 0) {
if (!g_wifi_default_wpa_crypto_funcs.aes_wrap) {
ESP_LOGE(TAG, "Pairing follow-up: aes_wrap not registered");
return ESP_FAIL;
}
if (g_wifi_default_wpa_crypto_funcs.aes_wrap(saved->kek, saved->kek_len, plain_len / 8, plain, wrapped) != 0) {
return ESP_FAIL;
}
@@ -724,20 +663,17 @@ static esp_err_t nan_app_send_pairing_followup(uint8_t svc_id, uint8_t peer_svc_
memcpy(w, wrapped, wrapped_len);
w += wrapped_len;
/* SSI: GSP (WFA OUI + proto=Generic) carrying an SRV record so iPhone
* has Transport Port + Instance Name to associate with this pairing. */
srv_ssi_len = nan_pairing_build_srv_ssi(srv_ssi, sizeof(srv_ssi),
NAN_PAIRING_SRV_HOSTNAME,
NAN_PAIRING_SRV_PORT);
/* SSI: GSP (WFA OUI + proto=Generic). */
srv_ssi_len = nan_pairing_build_srv_ssi((uint8_t *)&srv_ssi, sizeof(srv_ssi));
if (srv_ssi_len == 0) {
ESP_LOGW(TAG, "Pairing follow-up: failed to build SRV SSI");
ESP_LOGW(TAG, "Pairing follow-up: failed to build GSP SSI");
return ESP_FAIL;
}
fup.inst_id = svc_id;
fup.peer_inst_id = peer_svc_id;
MACADDR_COPY(fup.peer_mac, peer_mac);
fup.ssi = srv_ssi;
fup.ssi = (uint8_t *)&srv_ssi;
fup.ssi_len = (uint16_t)srv_ssi_len;
sk_attr_len = (size_t)(w - shared_key_wrapped);
@@ -747,13 +683,9 @@ static esp_err_t nan_app_send_pairing_followup(uint8_t svc_id, uint8_t peer_svc_
/* NIRA proves possession of the NIK we just wrapped above; iPhone uses
* it to bind the NIK to the sender and won't commit the pairing record
* without it. */
if (esp_wifi_get_mac(WIFI_IF_NAN, our_nmi) != ESP_OK) {
ESP_LOGW(TAG, "Pairing follow-up: cannot read NAN NMI for NIRA");
return ESP_FAIL;
}
nira_len = nan_pairing_build_nira_attr(nira_attr, sizeof(nira_attr),
nik, our_nmi);
* without it. esp_nan_construct_nira() reuses the same cached nonce/tag we
* advertise in sync discovery. */
nira_len = (size_t)esp_nan_construct_nira(nira_attr);
if (nira_len == 0) {
ESP_LOGW(TAG, "Pairing follow-up: NIRA attribute build failed");
return ESP_FAIL;
@@ -773,6 +705,7 @@ static void nan_app_send_pairing_followup_eloop(void *eloop_data, void *user_dat
if (!ctx) {
return;
}
(void) nan_app_send_pairing_followup(ctx->svc_id, ctx->peer_svc_id,
ctx->peer_mac,
ctx->shared_key_attr,
@@ -780,6 +713,142 @@ static void nan_app_send_pairing_followup_eloop(void *eloop_data, void *user_dat
os_free(ctx);
}
static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi,
uint8_t role,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len,
uint32_t nik_lifetime_sec)
{
if (!peer_nmi) {
return;
}
uint32_t lifetime_sec = nik_lifetime_sec ?
nik_lifetime_sec : NAN_PAIRING_DEFAULT_NIK_LIFETIME_SEC;
/* Cache ND-PMK for future paired NDPs (Wi-Fi Aware v4.0 §7.6.4.2). */
if (ndp_csid && nd_pmk && nd_pmk_len == ESP_WIFI_NAN_NDP_PMK_LEN) {
(void)nan_app_register_paired_peer(peer_nmi, role, ndp_csid,
nd_pmk, nd_pmk_len,
lifetime_sec);
} else {
ESP_LOGW(TAG, "Pairing complete for " MACSTR
": ND-PMK unavailable (csid=%u nd_pmk_len=%u); "
"paired-peer cache not updated",
MAC2STR(peer_nmi), ndp_csid, (unsigned)nd_pmk_len);
}
struct peer_svc_info *peer = nan_find_peer_svc(0, 0, (uint8_t *)peer_nmi);
struct own_svc_info *own = NULL;
if (peer) {
own = nan_find_own_svc(peer->own_svc_id);
}
if (own) {
if (!own->pairing.npk_nik_caching) {
wifi_event_nan_pairing_complete_t evt = {0};
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
evt.reason_code = 0;
MACADDR_COPY(evt.peer_nmi, peer_nmi);
esp_nan_complete_pairing(own->svc_id, peer->svc_id);
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
return;
}
}
if (role == NAN_ROLE_PAIRING_INITIATOR) {
struct nan_pairing_fup_ctx *ctx = os_zalloc(sizeof(*ctx));
if (!ctx) {
ESP_LOGW(TAG, "Pairing key installed: failed to alloc fup ctx for " MACSTR,
MAC2STR(peer_nmi));
return;
}
NAN_DATA_LOCK();
if (peer) {
ctx->svc_id = peer->own_svc_id;
ctx->peer_svc_id = peer->svc_id;
}
NAN_DATA_UNLOCK();
/* Without a peer service entry the follow-up would carry zero service
* IDs; drop it rather than send an invalid frame. */
if (!ctx->svc_id || !ctx->peer_svc_id) {
ESP_LOGW(TAG, "Pairing key installed: peer service not found for " MACSTR
", skipping initiator follow-up", MAC2STR(peer_nmi));
os_free(ctx);
return;
}
MACADDR_COPY(ctx->peer_mac, peer_nmi);
ctx->shared_key_attr_len = 0;
own = nan_find_own_svc(ctx->svc_id);
if (own) {
nan_pairing_arm_pending(own, peer_nmi);
}
if (eloop_register_timeout(0, 0, nan_app_send_pairing_followup_eloop, NULL, ctx) != 0) {
ESP_LOGW(TAG, "Pairing key installed: failed to schedule initiator follow-up");
if (own) {
nan_pairing_cancel_svc_pending(own);
}
os_free(ctx);
}
return;
}
if (role == NAN_ROLE_PAIRING_RESPONDER) {
NAN_DATA_LOCK();
if (peer) {
own = nan_find_own_svc(peer->own_svc_id);
}
NAN_DATA_UNLOCK();
if (own) {
nan_pairing_arm_pending(own, peer_nmi);
}
}
}
/* Insert or refresh a (peer NIK, NPK) entry in the in-RAM credential cache used
* for NIRA identity resolution. Caller holds NAN_DATA_LOCK. A @a npk of NULL
* stores a zeroed key. When the cache is full the oldest entry (slot 0) is
* reused. */
static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *npk, uint8_t service_hash[6])
{
wifi_nan_peer_creds_t *slot = NULL;
/* Reuse the slot already holding this NIK, if any. */
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
if (s_nan_ctx.peer_creds[i].is_valid &&
os_memcmp(s_nan_ctx.peer_creds[i].peer_nik, peer_nik, ESP_WIFI_NAN_NIK_LEN) == 0) {
slot = &s_nan_ctx.peer_creds[i];
break;
}
}
if (!slot) {
if (s_nan_ctx.num_peer_creds < ESP_WIFI_NAN_MAX_PEER_CREDS) {
slot = &s_nan_ctx.peer_creds[s_nan_ctx.num_peer_creds++];
} else {
slot = &s_nan_ctx.peer_creds[0];
}
}
memcpy(slot->peer_nik, peer_nik, ESP_WIFI_NAN_NIK_LEN);
memcpy(slot->service_hash, service_hash, 6);
if (npk) {
memcpy(slot->npk, npk, ESP_WIFI_NAN_NPK_LEN);
} else {
memset(slot->npk, 0, ESP_WIFI_NAN_NPK_LEN);
}
slot->is_valid = true;
}
void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
const uint8_t *peer_mac,
const uint8_t *shared_key_attr,
@@ -792,7 +861,6 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
uint32_t lifetime_sec = 0;
struct nan_pairing_fup_ctx *ctx;
size_t alloc_len;
if (!shared_key_attr || !peer_mac) {
return;
}
@@ -802,7 +870,6 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
if (shared_key_attr[0] != NAN_ATTR_ID_SHARED_KEY_DESC) {
return;
}
const uint16_t *attr_body_len_field = (const uint16_t *)&shared_key_attr[1];
attr_body_len = *attr_body_len_field;
@@ -823,18 +890,69 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
return;
}
bool already_had_nik = false;
bool pairing_completed = false;
bool persist_creds = false;
uint8_t persist_npk[ESP_WIFI_NAN_NPK_LEN] = {0};
struct own_svc_info *own = NULL;
NAN_DATA_LOCK();
struct peer_svc_info *p_peer_svc = nan_find_peer_svc_exact(svc_id, peer_svc_id, peer_mac);
if (p_peer_svc) {
already_had_nik = p_peer_svc->has_nik;
memcpy(p_peer_svc->peer_nik, nik, NAN_APP_PEER_NIK_LEN);
p_peer_svc->peer_nik_cipher_ver = cipher_ver;
p_peer_svc->peer_nik_lifetime_sec = lifetime_sec;
p_peer_svc->has_nik = true;
ESP_LOGI(TAG, "Stored peer NIK from " MACSTR " (cipher_ver=%u, lifetime=%u s)",
MAC2STR(peer_mac), cipher_ver, lifetime_sec);
/* Refresh the NIRA credential cache with this peer's NIK and, if the
* pairing record is available, its NPK. */
const struct nan_paired_peer *paired = nan_app_find_paired_peer(peer_mac);
if (paired) {
memcpy(persist_npk, paired->nd_pmk, ESP_WIFI_NAN_NPK_LEN);
}
own = nan_find_own_svc(p_peer_svc->own_svc_id);
if (!already_had_nik) {
pairing_completed = true;
}
nan_app_update_peer_creds(nik, paired ? paired->nd_pmk : NULL, own ? own->svc_hash : NULL);
persist_creds = s_nan_ctx.use_nvs_for_caching;
}
NAN_DATA_UNLOCK();
/* Persist outside the lock; NVS writes can block. */
if (persist_creds) {
esp_wifi_nan_save_creds_for_peer(nik, persist_npk, own ? own->svc_hash : NULL);
}
/* Invoke blocking calls outside NAN_DATA_LOCK to avoid deadlock. */
if (pairing_completed) {
wifi_event_nan_pairing_complete_t evt = {0};
if (own) {
nan_pairing_cancel_svc_pending(own);
}
evt.status = WIFI_NAN_PAIRING_STATUS_ACCEPTED;
evt.reason_code = 0;
MACADDR_COPY(evt.peer_nmi, peer_mac);
esp_nan_complete_pairing(p_peer_svc ? p_peer_svc->own_svc_id : 0,
p_peer_svc ? p_peer_svc->svc_id : peer_svc_id);
nan_app_post_event(WIFI_EVENT_NAN_PAIRING_CONFIRM, &evt, sizeof(evt));
}
/* Only reply with our own NIK the first time we receive the peer's.
* If has_nik was already true this is a redundant echo — don't reply
* or we create an infinite ping-pong of follow-ups. */
if (already_had_nik) {
ESP_LOGD(TAG, "Pairing follow-up: NIK already known for " MACSTR ", skipping reply",
MAC2STR(peer_mac));
return;
}
if (total_len > SIZE_MAX - sizeof(*ctx)) {
return;
}
@@ -855,6 +973,51 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
}
}
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING && CONFIG_ESP_WIFI_PASN_SUPPORT && CONFIG_ESP_WIFI_NAN_SECURITY */
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len)
{
uint8_t expected_tag[NAN_NIRA_TAG_LEN];
const uint8_t *nonce;
const uint8_t *received_tag;
bool match = false;
if (!peer_mac || !nira_attr) {
return false;
}
if (nira_attr_len < NAN_NIRA_ATTR_LEN) {
ESP_LOGW(TAG, "NIRA verify: attribute too short (%u < %u)",
(unsigned)nira_attr_len, (unsigned)NAN_NIRA_ATTR_LEN);
return false;
}
nonce = nira_attr + 4;
received_tag = nira_attr + 4 + NAN_NIRA_NONCE_LEN;
/* The peer derives its NIRA tag from one of its NIKs; the sending MAC may be
* randomised, so resolve the identity by trying every cached peer NIK. */
NAN_DATA_LOCK();
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_pairing_derive_nira_tag(s_nan_ctx.peer_creds[i].peer_nik, peer_mac,
nonce, expected_tag) != 0) {
continue;
}
if (os_memcmp_const(expected_tag, received_tag, NAN_NIRA_TAG_LEN) == 0) {
match = true;
break;
}
}
NAN_DATA_UNLOCK();
if (match) {
ESP_LOGD(TAG, "NIRA verify: OK for "MACSTR, MAC2STR(peer_mac));
} else {
ESP_LOGD(TAG, "NIRA verify: no matching NIK for "MACSTR, MAC2STR(peer_mac));
}
return match;
}
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -99,36 +99,6 @@ static bool nan_csid_bitmap_has_pasn(uint16_t csid_bitmap)
return (csid_bitmap & WIFI_NAN_CSID_BIT_NCS_PK_PASN_128) != 0;
}
/*
* Service ID = first 6 bytes of SHA256(lowercase(service_name))
* per Wi-Fi Aware v4.0 §5.1.5 (Service Name and Service ID).
*/
static bool nan_compute_service_id(const char *service_name, uint8_t service_id[6])
{
if (!service_name || !g_wifi_default_wpa_crypto_funcs.sha256_vector) {
return false;
}
size_t name_len = strlen(service_name);
char *lower = os_malloc(name_len + 1);
if (!lower) {
return false;
}
strlcpy(lower, service_name, name_len + 1);
for (char *p = lower; *p; p++) {
*p = tolower((unsigned char) * p);
}
uint8_t hash[32];
const uint8_t *addr[1] = {(const uint8_t *)lower};
size_t len[1] = {name_len};
int ret = g_wifi_default_wpa_crypto_funcs.sha256_vector(1, addr, len, hash);
os_free(lower);
if (ret != 0) {
return false;
}
memcpy(service_id, hash, 6);
return true;
}
#define NAN_SERVICE_ID_LEN 6
/*
@@ -457,6 +427,91 @@ static bool nan_security_fill_from_paired_cache(struct ndl_info *ndl, const uint
}
#endif
static bool nan_ndp_resp_resolve_pmk(struct ndl_info *ndl, const uint8_t *peer_nmi)
{
static const uint8_t zero_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN] = {0};
static const uint8_t zero_pmk[ESP_WIFI_NAN_NDP_PMK_LEN] = {0};
if (!ndl || !peer_nmi) {
return false;
}
bool have_peer_pmkid = (memcmp(ndl->security_ctx.nd_pmkid, zero_pmkid,
ESP_WIFI_NAN_NDP_PMKID_LEN) != 0);
bool need_pmk = (ndl->security_ctx.type != WIFI_NAN_SECURITY_ENCRYPTED) ||
(memcmp(ndl->security_ctx.nd_pmk, zero_pmk, ESP_WIFI_NAN_NDP_PMK_LEN) == 0);
if (!need_pmk) {
return have_peer_pmkid;
}
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
if (nan_security_fill_from_paired_cache(ndl, peer_nmi)) {
return have_peer_pmkid;
}
#endif
if (!have_peer_pmkid) {
return false;
}
struct own_svc_info *p_svc = nan_find_own_svc(ndl->publisher_id);
int matched_idx = p_svc ? nan_match_pmkid(p_svc, ndl->security_ctx.nd_pmkid,
ndl->peer_nmi, ndl->peer_ndi) : -1;
if (matched_idx < 0) {
return false;
}
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
ndl->security_ctx.csid_bitmap = p_svc->derived_security[matched_idx].csid_bitmap;
memcpy(ndl->security_ctx.nd_pmk,
p_svc->derived_security[matched_idx].nd_pmk,
ESP_WIFI_NAN_NDP_PMK_LEN);
return true;
}
uint8_t esp_nan_get_ndp_resp_num_pmkids(uint8_t ndp_id, const uint8_t *peer_nmi)
{
static const uint8_t zero_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN] = {0};
if (!peer_nmi) {
return 0;
}
NAN_DATA_LOCK();
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
if (!ndl) {
ndl = nan_find_ndl(0, (uint8_t *)peer_nmi);
}
uint8_t num = 0;
if (ndl && memcmp(ndl->security_ctx.nd_pmkid, zero_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN) != 0) {
num = 1;
}
NAN_DATA_UNLOCK();
return num;
}
uint32_t esp_nan_get_ndp_resp_shared_key_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi)
{
if (!peer_nmi) {
return 0;
}
NAN_DATA_LOCK();
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
if (!ndl) {
ndl = nan_find_ndl(0, (uint8_t *)peer_nmi);
}
if (!ndl || ndl->handshake_state != NAN_HANDSHAKE_M1_RCVD ||
!nan_ndp_resp_resolve_pmk(ndl, peer_nmi)) {
NAN_DATA_UNLOCK();
return 0;
}
NAN_DATA_UNLOCK();
return esp_nan_get_shared_key_desc_attr_len(0);
}
/*
* Build RSNA Key Descriptor payload (95-byte EAPOL-Key layout, see
* IEEE 802.11-2020 §12.7.2). NAN carries this body inside the NAN
@@ -1164,9 +1219,9 @@ int esp_nan_ndp_security_install_get_shared_desc_len(void)
int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t ndp_id, const uint8_t *peer_nmi)
{
const uint32_t attr_len = esp_nan_get_shared_key_desc_attr_len(0);
const uint32_t attr_len = esp_nan_get_ndp_resp_shared_key_desc_len(ndp_id, peer_nmi);
if (!buf || buf_len < attr_len || !peer_nmi) {
if (!buf || !peer_nmi || attr_len == 0 || buf_len < attr_len) {
return 0;
}
@@ -1182,50 +1237,10 @@ int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t n
ESP_LOGW(TAG, "NDP Resp Key Desc: NDL not in M1_RCVD (state=%d)", ndl->handshake_state);
return 0;
}
/* Resolve PMK from publish when: NDL not encrypted, or encrypted but nd_pmk not set */
{
static const uint8_t zero_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN] = {0};
static const uint8_t zero_pmk[ESP_WIFI_NAN_NDP_PMK_LEN] = {0};
bool have_peer_pmkid = (memcmp(ndl->security_ctx.nd_pmkid, zero_pmkid,
ESP_WIFI_NAN_NDP_PMKID_LEN) != 0);
bool need_pmk = (ndl->security_ctx.type != WIFI_NAN_SECURITY_ENCRYPTED) ||
(memcmp(ndl->security_ctx.nd_pmk, zero_pmk, ESP_WIFI_NAN_NDP_PMK_LEN) == 0);
bool resolved_from_pairing_cache = false;
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
if (need_pmk) {
resolved_from_pairing_cache = nan_security_fill_from_paired_cache(ndl, peer_nmi);
}
#endif
if (resolved_from_pairing_cache) {
ESP_LOGD(TAG, "NDP Resp Key Desc: resolved PMK from paired-peer cache");
} else if (need_pmk && have_peer_pmkid) {
struct own_svc_info *p_svc = nan_find_own_svc(ndl->publisher_id);
int matched_idx = p_svc ? nan_match_pmkid(p_svc, ndl->security_ctx.nd_pmkid,
ndl->peer_nmi, ndl->peer_ndi) : -1;
if (matched_idx >= 0) {
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
ndl->security_ctx.csid_bitmap = p_svc->derived_security[matched_idx].csid_bitmap;
memcpy(ndl->security_ctx.nd_pmk,
p_svc->derived_security[matched_idx].nd_pmk,
ESP_WIFI_NAN_NDP_PMK_LEN);
ESP_LOGD(TAG, "NDP Resp Key Desc: resolved PMK from cred slot %d", matched_idx);
} else if (ndl->security_ctx.type != WIFI_NAN_SECURITY_ENCRYPTED) {
NAN_DATA_UNLOCK();
ESP_LOGW(TAG, "NDP Resp Key Desc: NDL not encrypted; send NDP Response without Shared Key Descriptor");
return 0;
} else {
NAN_DATA_UNLOCK();
ESP_LOGW(TAG, "NDP Resp Key Desc: no PMK for peer PMKID; ensure matching credential on both devices");
return 0;
}
} else if (ndl->security_ctx.type != WIFI_NAN_SECURITY_ENCRYPTED) {
NAN_DATA_UNLOCK();
ESP_LOGW(TAG, "NDP Resp Key Desc: NDL not encrypted; send NDP Response without Shared Key Descriptor");
return 0;
}
if (!nan_ndp_resp_resolve_pmk(ndl, peer_nmi)) {
NAN_DATA_UNLOCK();
ESP_LOGW(TAG, "NDP Resp Key Desc: no PMK for peer PMKID; ensure matching credential on both devices");
return 0;
}
/* Generate SNonce and derive PTK if not yet done */
@@ -599,10 +599,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -275,7 +275,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -811,14 +811,6 @@ config SOC_COEX_HW_PTI
bool
default y
config SOC_EXTERNAL_COEX_ADVANCE
bool
default n
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -361,8 +361,6 @@
#define SOC_COEX_HW_PTI (1)
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (0) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -1335,10 +1335,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH
int
default 12
@@ -541,7 +541,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
// #define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -1071,10 +1071,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -442,7 +442,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -983,10 +983,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -411,7 +411,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -1079,10 +1079,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -469,7 +469,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -951,10 +951,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -444,7 +444,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -1127,10 +1127,6 @@ config SOC_EXTERNAL_COEX_ADVANCE
bool
default y
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default n
config SOC_PHY_DIG_REGS_MEM_SIZE
int
default 21
@@ -478,7 +478,6 @@
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (1) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (0) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*--------------- PHY REGISTER AND MEMORY SIZE CAPS --------------------------*/
#define SOC_PHY_DIG_REGS_MEM_SIZE (21*4)
@@ -979,10 +979,6 @@ config SOC_COEX_HW_PTI
bool
default y
config SOC_EXTERNAL_COEX_ADVANCE
bool
default n
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default y
@@ -440,7 +440,6 @@
// No contents
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (0) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (1) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*-------------------------- Temperature Sensor CAPS -------------------------------------*/
@@ -1139,10 +1139,6 @@ config SOC_COEX_HW_PTI
bool
default y
config SOC_EXTERNAL_COEX_ADVANCE
bool
default n
config SOC_EXTERNAL_COEX_LEADER_TX_LINE
bool
default y
@@ -471,7 +471,6 @@
#define SOC_COEX_HW_PTI (1)
/*-------------------------- EXTERNAL COEXISTENCE CAPS -------------------------------------*/
#define SOC_EXTERNAL_COEX_ADVANCE (0) /*!< HARDWARE ADVANCED EXTERNAL COEXISTENCE CAPS */
#define SOC_EXTERNAL_COEX_LEADER_TX_LINE (1) /*!< EXTERNAL COEXISTENCE TX LINE CAPS */
/*-------------------------- SDMMC CAPS -----------------------------------------*/
+5 -1
View File
@@ -314,7 +314,11 @@ endif()
if(CONFIG_ESP_WIFI_ENABLE_SAE_H2E)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE_H2E)
endif()
if(CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT)
# PASN reuses the SAE module (PWE/crypto + comeback-token mechanism), so
# CONFIG_SAE must be defined whenever SoftAP-SAE or PASN is enabled. AP-side SAE
# authentication paths are additionally gated on CONFIG_ESP_WIFI_SOFTAP_SUPPORT
# in the sources so they stay out of PASN-only (SoftAP-disabled) builds.
if(CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT OR CONFIG_ESP_WIFI_PASN_SUPPORT)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE)
endif()
if(CONFIG_ESP_WIFI_WPA3_COMPATIBLE_SUPPORT)
@@ -64,14 +64,19 @@ enum nan_role {
* @param role enum nan_role value for the local device.
* @param ndp_csid NCS-SK CSID for paired-peer NDP (WIFI_NAN_CSID_NCS_SK_128
* or _SK_256), 0 if no usable cipher mapping was available.
* @param nd_pmk ND-PMK bytes (32) or NULL if KDK was absent.
* @param nd_pmk_len Length of @a nd_pmk (32 when present, 0 otherwise).
* @param nd_pmk ND-PMK bytes (32) or NULL if KDK was absent.
* @param nd_pmk_len Length of @a nd_pmk (32 when present, 0 otherwise).
* @param nik_lifetime_sec NIK / paired-peer cache lifetime in seconds, as supplied
* to @ref esp_nan_supp_pasn_initiator_auth or
* @ref esp_nan_supp_pasn_responder_init. The NAN app
* substitutes 86400 s when this is 0.
*/
typedef void (*esp_nan_pairing_key_installed_cb_t)(const uint8_t *peer_nmi,
uint8_t role,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len);
size_t nd_pmk_len,
uint32_t nik_lifetime_sec);
/**
* Last PASN key material after successful pairing (PMK + flattened PTK KCK|KEK|TK|KDK).
@@ -98,11 +103,15 @@ struct nan_pasn_key_material {
* pairing responder. Runs on the wpa_supplicant eloop thread.
*
* @param peer_nmi Peer NMI (6 bytes).
* @param pincode 6-digit PIN (0..999999), or @c UINT32_MAX for the default PIN.
* @param pincode 6-digit PIN (0..999999), or @c UINT32_MAX for the default PIN.
* @param nik_lifetime_sec NIK lifetime in seconds; forwarded unchanged to
* @c pairing_key_installed_cb (NAN app currently
* passes 86400).
* @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_responder_init(const uint8_t *peer_nmi, uint32_t pincode,
uint32_t nik_lifetime_sec,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
/**
@@ -113,11 +122,15 @@ int esp_nan_supp_pasn_responder_init(const uint8_t *peer_nmi, uint32_t pincode,
* Runs on the wpa_supplicant eloop thread.
*
* @param peer_nmi Peer NMI (6 bytes).
* @param pincode 6-digit PIN (0..999999), or @c UINT32_MAX for the default PIN.
* @param pincode 6-digit PIN (0..999999), or @c UINT32_MAX for the default PIN.
* @param nik_lifetime_sec NIK lifetime in seconds; forwarded unchanged to
* @c pairing_key_installed_cb (NAN app currently
* passes 86400).
* @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_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);
/**
@@ -43,6 +43,7 @@ struct nan_pasn_data {
size_t pasn_ptk_len;
struct pasn_data *pasn;
nan_pasn_pairing_key_installed_cb_t pairing_key_installed_cb;
uint32_t nik_lifetime_sec;
};
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr,
@@ -1254,7 +1254,8 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
nan->pairing_key_installed_cb(pasn->peer_addr,
(uint8_t)nan->dev_role,
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
nd_pmk, nd_pmk_len);
nd_pmk, nd_pmk_len,
nan->nik_lifetime_sec);
}
forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
nan_pasn_data_deinit(nan);
@@ -1311,7 +1312,8 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
nan->pairing_key_installed_cb(pasn->peer_addr,
(uint8_t)nan->dev_role,
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
nd_pmk, nd_pmk_len);
nd_pmk, nd_pmk_len,
nan->nik_lifetime_sec);
}
}
#ifdef CONFIG_TESTING_OPTIONS
@@ -1550,6 +1552,7 @@ int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, i
struct nan_pasn_eloop_ctx {
uint8_t peer_addr[ETH_ALEN];
uint32_t pincode;
uint32_t nik_lifetime_sec;
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb;
};
@@ -1580,10 +1583,11 @@ static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
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 % 1000000U));
(unsigned)ctx->pincode);
if (os_snprintf_error(sizeof(pin_digits), n)) {
nan_pasn_data_deinit(pd);
os_free(ctx);
@@ -1610,6 +1614,7 @@ static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
}
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)
{
struct nan_pasn_eloop_ctx *ctx;
@@ -1621,6 +1626,7 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
os_memcpy(ctx->peer_addr, peer_nmi, ETH_ALEN);
ctx->pincode = pincode;
ctx->nik_lifetime_sec = nik_lifetime_sec;
ctx->pairing_key_installed_cb = pairing_key_installed_cb;
if (eloop_register_timeout(0, 0, nan_pasn_auth_eloop_cb, NULL, ctx) != 0) {
@@ -1740,7 +1746,7 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
if (pincode != UINT32_MAX) {
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
(unsigned)(pincode % 1000000U));
(unsigned)pincode);
if (os_snprintf_error(sizeof(pin_digits), n)) {
goto fail;
}
@@ -1773,6 +1779,7 @@ fail:
struct pasn_responder_eloop_ctx {
uint8_t peer_addr[ETH_ALEN];
uint32_t pincode;
uint32_t nik_lifetime_sec;
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb;
};
@@ -1789,12 +1796,14 @@ static void pasn_responder_init_eloop_cb(void *eloop_ctx, void *user_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;
}
}
os_free(ctx);
}
int esp_nan_supp_pasn_responder_init(const uint8_t *peer_nmi, uint32_t pincode,
uint32_t nik_lifetime_sec,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
{
struct pasn_responder_eloop_ctx *ctx;
@@ -1805,6 +1814,7 @@ int esp_nan_supp_pasn_responder_init(const uint8_t *peer_nmi, uint32_t pincode,
}
ctx->pincode = pincode;
ctx->nik_lifetime_sec = nik_lifetime_sec;
os_memcpy(ctx->peer_addr, peer_nmi, ETH_ALEN);
ctx->pairing_key_installed_cb = pairing_key_installed_cb;
@@ -240,6 +240,13 @@ typedef enum {
NAN_KEY_NM_TK,
} nan_key_type_t;
typedef struct {
uint8_t peer_nik[ESP_WIFI_NAN_NIK_LEN]; /**< Peer's NAN Identity Key (16 bytes) */
uint8_t npk[ESP_WIFI_NAN_NPK_LEN]; /**< NAN Pairwise Key / NCS-SK PMK (32 bytes) */
uint8_t service_hash[6]; /**< Service Hash of the corresponding service */
bool is_valid; /**< True if this credential entry is valid */
} wifi_nan_peer_creds_t;
typedef wifi_scan_channel_bitmap_t channel_bitmap_t;
uint8_t *esp_wifi_ap_get_prof_pmk_internal(void);
@@ -334,5 +341,12 @@ 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_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]);
esp_err_t esp_wifi_nan_save_creds_for_peer(const uint8_t peer_nik[ESP_WIFI_NAN_NIK_LEN],
const uint8_t npk[ESP_WIFI_NAN_NPK_LEN], const uint8_t service_hash[6]);
esp_err_t esp_wifi_nan_erase_all_creds(void);
#endif /* _ESP_WIFI_DRIVER_H_ */
@@ -441,7 +441,11 @@ void esp_wifi_unregister_wpa3_cb(void)
}
#endif /* CONFIG_WPA3_SAE */
#ifdef CONFIG_SAE
/* AP-side (hostap) SAE authentication. CONFIG_SAE may also be enabled for PASN
* with SoftAP disabled, but this block both defines and calls SoftAP-only
* primitives (esp_send_sae_auth_reply, handle_auth_sae), so additionally gate
* it on CONFIG_ESP_WIFI_SOFTAP_SUPPORT. */
#if defined(CONFIG_SAE) && defined(CONFIG_ESP_WIFI_SOFTAP_SUPPORT)
static TaskHandle_t g_wpa3_hostap_task_hdl = NULL;
static QueueHandle_t g_wpa3_hostap_evt_queue = NULL;
@@ -861,4 +865,4 @@ void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb)
wpa_cb->wpa3_hostap_handle_auth = wpa3_hostap_handle_auth;
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
@@ -6,6 +6,7 @@
#ifndef ESP_WPA3_H
#define ESP_WPA3_H
#include "sdkconfig.h"
#include "esp_wifi_driver.h"
#ifdef CONFIG_WPA3_SAE
@@ -27,7 +28,10 @@ static inline void esp_wpa3_free_sae_data(void)
#endif /* CONFIG_WPA3_SAE */
#ifdef CONFIG_SAE
/* AP-side (hostap) SAE definitions require SoftAP; CONFIG_SAE alone may be set
* for PASN. The #else branch provides a no-op esp_wifi_register_wpa3_ap_cb()
* stub so callers link in PASN-only (SoftAP-disabled) builds. */
#if defined(CONFIG_SAE) && defined(CONFIG_ESP_WIFI_SOFTAP_SUPPORT)
enum SIG_WPA3_TASK {
SIG_WPA3_RX_COMMIT,
SIG_WPA3_RX_CONFIRM,
@@ -58,12 +62,12 @@ void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb);
int wpa3_hostap_auth_init(void *data);
bool wpa3_hostap_auth_deinit(void);
#else /* CONFIG_SAE */
#else /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
static inline void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb)
{
wpa_cb->wpa3_hostap_handle_auth = NULL;
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
#endif /* ESP_WPA3_H */
@@ -28,7 +28,10 @@
#define OWE_DH_GRP19 19
#endif
#ifdef CONFIG_SAE
/* AP-side SAE authentication. Requires SoftAP: CONFIG_SAE may also be enabled
* for PASN (SoftAP disabled), but this block calls SoftAP-only primitives
* (e.g. esp_send_sae_auth_reply), so gate it on CONFIG_ESP_WIFI_SOFTAP_SUPPORT. */
#if defined(CONFIG_SAE) && defined(CONFIG_ESP_WIFI_SOFTAP_SUPPORT)
static void sae_set_state(struct sta_info *sta, enum sae_state state,
const char *reason)
@@ -778,7 +781,7 @@ queued:
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
u16 wpa_res_to_status_code(enum wpa_validate_result res)
{
@@ -45,6 +45,8 @@ const wpa_crypto_funcs_t g_wifi_default_wpa_crypto_funcs = {
.ccmp_encrypt = (esp_ccmp_encrypt_t)ccmp_encrypt,
.aes_gmac = (esp_aes_gmac_t)esp_aes_gmac,
.sha256_vector = (esp_sha256_vector_t)sha256_vector,
.aes_wrap = (esp_aes_wrap_t)aes_wrap,
.aes_unwrap = (esp_aes_unwrap_t)aes_unwrap,
};
const mesh_crypto_funcs_t g_wifi_default_mesh_crypto_funcs = {
@@ -54,7 +54,7 @@ void pasn_initiator_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa,
{
struct rsn_pmksa_cache_entry *entry;
entry = pmksa_cache_get(pmksa, bssid, NULL, NULL);
entry = pmksa_cache_get(pmksa, bssid, NULL, NULL, NULL);
if (!entry)
return;
@@ -68,7 +68,7 @@ int pasn_initiator_pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
{
struct rsn_pmksa_cache_entry *entry;
entry = pmksa_cache_get(pmksa, bssid, NULL, NULL);
entry = pmksa_cache_get(pmksa, bssid, NULL, NULL, NULL);
if (entry) {
os_memcpy(pmkid, entry->pmkid, PMKID_LEN);
os_memcpy(pmk, entry->pmk, entry->pmk_len);
@@ -627,7 +627,8 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
struct rsn_pmksa_cache_entry *pmksa;
pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr, NULL, NULL);
pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr, pasn->own_addr,
NULL, NULL);
if (pmksa && pasn->custom_pmkid_valid)
pmkid = pasn->custom_pmkid;
else if (pmksa)
@@ -900,7 +901,7 @@ static int wpas_pasn_set_pmk(struct pasn_data *pasn,
pmkid = rsn_data->pmkid;
}
pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr,
pmksa = pmksa_cache_get(pasn->pmksa, pasn->peer_addr, pasn->own_addr,
pmkid, NULL);
if (pmksa) {
wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");