diff --git a/components/esp_coex/esp32/esp_coex_adapter.c b/components/esp_coex/esp32/esp_coex_adapter.c index fbd93f6a6ad..65c1fa40021 100644 --- a/components/esp_coex/esp32/esp_coex_adapter.c +++ b/components/esp_coex/esp32/esp_coex_adapter.c @@ -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; diff --git a/components/esp_coex/esp32c5/esp_coex_adapter.c b/components/esp_coex/esp32c5/esp_coex_adapter.c index 8588b7348d1..48b871c8a44 100644 --- a/components/esp_coex/esp32c5/esp_coex_adapter.c +++ b/components/esp_coex/esp32c5/esp_coex_adapter.c @@ -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)); diff --git a/components/esp_coex/esp32c61/esp_coex_adapter.c b/components/esp_coex/esp32c61/esp_coex_adapter.c index 059160f7bab..2778940239d 100644 --- a/components/esp_coex/esp32c61/esp_coex_adapter.c +++ b/components/esp_coex/esp32c61/esp_coex_adapter.c @@ -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)); diff --git a/components/esp_coex/esp32h4/esp_coex_adapter.c b/components/esp_coex/esp32h4/esp_coex_adapter.c index f9592d2624b..49bb8130669 100644 --- a/components/esp_coex/esp32h4/esp_coex_adapter.c +++ b/components/esp_coex/esp32h4/esp_coex_adapter.c @@ -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)); diff --git a/components/esp_coex/esp32s31/esp_coex_adapter.c b/components/esp_coex/esp32s31/esp_coex_adapter.c index 858c8714c3c..ec8eddb75cd 100644 --- a/components/esp_coex/esp32s31/esp_coex_adapter.c +++ b/components/esp_coex/esp32s31/esp_coex_adapter.c @@ -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; diff --git a/components/esp_coex/test_apps/.build-test-rules.yml b/components/esp_coex/test_apps/.build-test-rules.yml index 21737a4ab0c..8aa670ac94b 100644 --- a/components/esp_coex/test_apps/.build-test-rules.yml +++ b/components/esp_coex/test_apps/.build-test-rules.yml @@ -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 diff --git a/components/esp_coex/test_apps/external_coex_function/README.md b/components/esp_coex/test_apps/external_coex_function/README.md index a0d10bf872d..0f69904fb59 100644 --- a/components/esp_coex/test_apps/external_coex_function/README.md +++ b/components/esp_coex/test_apps/external_coex_function/README.md @@ -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 | +| ----------------- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | --------- | diff --git a/components/esp_coex/test_apps/external_coex_function/main/test_enable_extern_coex.c b/components/esp_coex/test_apps/external_coex_function/main/test_enable_extern_coex.c index 84706ad490d..6a59384de6c 100644 --- a/components/esp_coex/test_apps/external_coex_function/main/test_enable_extern_coex.c +++ b/components/esp_coex/test_apps/external_coex_function/main/test_enable_extern_coex.c @@ -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; diff --git a/components/esp_coex/test_apps/external_coex_function/pytest_external_coex_function.py b/components/esp_coex/test_apps/external_coex_function/pytest_external_coex_function.py index 0a527748231..2f3bfdae1df 100644 --- a/components/esp_coex/test_apps/external_coex_function/pytest_external_coex_function.py +++ b/components/esp_coex/test_apps/external_coex_function/pytest_external_coex_function.py @@ -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() diff --git a/components/esp_rom/esp32c2/ld/esp32c2.rom.ld b/components/esp_rom/esp32c2/ld/esp32c2.rom.ld index 26db884e98f..d2d42a1b3d1 100644 --- a/components/esp_rom/esp32c2/ld/esp32c2.rom.ld +++ b/components/esp_rom/esp32c2/ld/esp32c2.rom.ld @@ -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; diff --git a/components/esp_rom/esp32c3/ld/esp32c3.rom.ld b/components/esp_rom/esp32c3/ld/esp32c3.rom.ld index 5ef756f3b3f..1cbeb614c12 100644 --- a/components/esp_rom/esp32c3/ld/esp32c3.rom.ld +++ b/components/esp_rom/esp32c3/ld/esp32c3.rom.ld @@ -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; diff --git a/components/esp_rom/esp32c5/ld/esp32c5.rom.pp.ld b/components/esp_rom/esp32c5/ld/esp32c5.rom.pp.ld index d710b70b722..9e276e1c605 100644 --- a/components/esp_rom/esp32c5/ld/esp32c5.rom.pp.ld +++ b/components/esp_rom/esp32c5/ld/esp32c5.rom.pp.ld @@ -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; diff --git a/components/esp_rom/esp32c61/ld/esp32c61.rom.pp.ld b/components/esp_rom/esp32c61/ld/esp32c61.rom.pp.ld index 56dd3a26ef7..432152de049 100644 --- a/components/esp_rom/esp32c61/ld/esp32c61.rom.pp.ld +++ b/components/esp_rom/esp32c61/ld/esp32c61.rom.pp.ld @@ -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;*/ diff --git a/components/esp_rom/esp32s3/ld/esp32s3.rom.ld b/components/esp_rom/esp32s3/ld/esp32s3.rom.ld index 2b9b441d211..8f30b35f71b 100644 --- a/components/esp_rom/esp32s3/ld/esp32s3.rom.ld +++ b/components/esp_rom/esp32s3/ld/esp32s3.rom.ld @@ -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; diff --git a/components/esp_rom/esp32s31/ld/esp32s31.rom.pp.ld b/components/esp_rom/esp32s31/ld/esp32s31.rom.pp.ld index b2f02c60683..7f1f71b0971 100644 --- a/components/esp_rom/esp32s31/ld/esp32s31.rom.pp.ld +++ b/components/esp_rom/esp32s31/ld/esp32s31.rom.pp.ld @@ -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; diff --git a/components/esp_wifi/include/esp_private/ftm_calibration_data.h b/components/esp_wifi/include/esp_private/ftm_calibration_data.h index 53297fd4f81..e08f099d553 100644 --- a/components/esp_wifi/include/esp_private/ftm_calibration_data.h +++ b/components/esp_wifi/include/esp_private/ftm_calibration_data.h @@ -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 *******************************************************/ diff --git a/components/esp_wifi/include/esp_private/wifi.h b/components/esp_wifi/include/esp_private/wifi.h index a8eb783f9a3..cdc7a9ad66d 100644 --- a/components/esp_wifi/include/esp_private/wifi.h +++ b/components/esp_wifi/include/esp_private/wifi.h @@ -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. * diff --git a/components/esp_wifi/include/esp_wifi_crypto_types.h b/components/esp_wifi/include/esp_wifi_crypto_types.h index a85b443aeb9..c260493c04a 100644 --- a/components/esp_wifi/include/esp_wifi_crypto_types.h +++ b/components/esp_wifi/include/esp_wifi_crypto_types.h @@ -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; /** diff --git a/components/esp_wifi/include/esp_wifi_types_generic.h b/components/esp_wifi/include/esp_wifi_types_generic.h index ac5c111f566..f44b8590de5 100644 --- a/components/esp_wifi/include/esp_wifi_types_generic.h +++ b/components/esp_wifi/include/esp_wifi_types_generic.h @@ -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) */ diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index e54196faa03..9ff50d7c694 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit e54196faa039e7836847a2920da749e4abbb3d96 +Subproject commit 9ff50d7c6943a64f11a5962053da1be6f5687493 diff --git a/components/esp_wifi/remote/include/injected/esp_wifi_crypto_types.h b/components/esp_wifi/remote/include/injected/esp_wifi_crypto_types.h index a85b443aeb9..c260493c04a 100644 --- a/components/esp_wifi/remote/include/injected/esp_wifi_crypto_types.h +++ b/components/esp_wifi/remote/include/injected/esp_wifi_crypto_types.h @@ -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; /** diff --git a/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h b/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h index 6debdfbecb3..b748bb9291d 100644 --- a/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h +++ b/components/esp_wifi/remote/include/injected/esp_wifi_types_generic.h @@ -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) */ diff --git a/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h b/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h index cd726f58b1c..243ef96af99 100644 --- a/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h +++ b/components/esp_wifi/wifi_apps/include/apps_private/wifi_apps_private.h @@ -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 } diff --git a/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h index df4b1fac425..a1cdabb441b 100644 --- a/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h +++ b/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h @@ -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) diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c index a8a137ad20e..c185d103a56 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_app.c @@ -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; diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h index 80064086d8a..414713635d5 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_i.h @@ -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 } diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c index 53c26be1959..fcca2e10ec1 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_pairing.c @@ -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 */ diff --git a/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c b/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c index fa408b91742..9ec1cdb3ec8 100644 --- a/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c +++ b/components/esp_wifi/wifi_apps/nan_app/src/nan_security.c @@ -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 */ diff --git a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in index 83ce987cea9..118f6a535db 100644 --- a/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c2/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32c2/include/soc/soc_caps.h b/components/soc/esp32c2/include/soc/soc_caps.h index 943d5f27948..9cc0ac01d98 100644 --- a/components/soc/esp32c2/include/soc/soc_caps.h +++ b/components/soc/esp32c2/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in index a825a03208b..8329fa213c6 100644 --- a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index ef9c55eb48d..95f472f90e6 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 9e9766922f7..81687faedcf 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 0a88b3f2cd3..be85b0921ce 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 9c6d7259615..e031cfeb211 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index 4144853696f..4736fd8ff30 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 4750a131bd0..5fb1f9e222f 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index aedc36add10..705a36e07b6 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 51b7d0f2f8a..36e7b09e47f 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index e25952bae16..ba4f0d68352 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in index b71665140b1..c19b2c8e0fd 100644 --- a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32h21/include/soc/soc_caps.h b/components/soc/esp32h21/include/soc/soc_caps.h index 90fde75f781..93dbb1483ab 100644 --- a/components/soc/esp32h21/include/soc/soc_caps.h +++ b/components/soc/esp32h21/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in index 66f94cdea15..affe94d8b0d 100644 --- a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32h4/include/soc/soc_caps.h b/components/soc/esp32h4/include/soc/soc_caps.h index 5164c60b63a..48590968076 100644 --- a/components/soc/esp32h4/include/soc/soc_caps.h +++ b/components/soc/esp32h4/include/soc/soc_caps.h @@ -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) diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in index 87efef0536c..11f54579ec4 100644 --- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 631f7aff845..bb6235e9946 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -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 -------------------------------------*/ diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in index fe0a48c1b46..b1a311ba9b2 100644 --- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 228893254ed..4a59b9a6ce6 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -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 -----------------------------------------*/ diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 8a9b5829334..85eef8e5246 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -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) diff --git a/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h b/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h index 1ab35e54cfa..865b93ac18b 100644 --- a/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h +++ b/components/wpa_supplicant/esp_supplicant/include/esp_private/esp_supp_nan.h @@ -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); /** diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h index 92accd57134..044cdcf1c83 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supp_i.h @@ -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, diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c index 3a3ea9d2edd..b5e2eee2840 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_nan_supplicant.c @@ -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; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h index dcf3fed52f7..909183f78c5 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h @@ -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_ */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c index 895955d6d28..32430eb7445 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c @@ -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 */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h index 9f5e49018d2..26486207e3b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h @@ -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 */ diff --git a/components/wpa_supplicant/src/ap/ieee802_11.c b/components/wpa_supplicant/src/ap/ieee802_11.c index 99327c8acbb..4ba44dad40e 100644 --- a/components/wpa_supplicant/src/ap/ieee802_11.c +++ b/components/wpa_supplicant/src/ap/ieee802_11.c @@ -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) { diff --git a/components/wpa_supplicant/src/crypto/crypto_ops.c b/components/wpa_supplicant/src/crypto/crypto_ops.c index 0346d17f5c1..ec3a3aee9f7 100644 --- a/components/wpa_supplicant/src/crypto/crypto_ops.c +++ b/components/wpa_supplicant/src/crypto/crypto_ops.c @@ -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 = { diff --git a/components/wpa_supplicant/src/pasn/pasn_initiator.c b/components/wpa_supplicant/src/pasn/pasn_initiator.c index c75983c1ae5..47faab5e619 100644 --- a/components/wpa_supplicant/src/pasn/pasn_initiator.c +++ b/components/wpa_supplicant/src/pasn/pasn_initiator.c @@ -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");