From 942d68a9c490686f03dd4bac5b7c5ff2f1dba353 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Fri, 28 Nov 2025 11:40:46 +0100 Subject: [PATCH 01/14] manifest: Update sdk-zephyr revision Pull the last minute changes in sdk-zephyr for the 3.2.0 release. Signed-off-by: Robert Lubos --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index f56cf02c5fc..f930a5ecc84 100644 --- a/west.yml +++ b/west.yml @@ -64,7 +64,7 @@ manifest: # https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html - name: zephyr repo-path: sdk-zephyr - revision: 428ce9f27bc368f8023de98cdd494b060374962b + revision: 56fbb4f3c7bba12fb34a5cd111b4ea9f7ad52162 import: # In addition to the zephyr repository itself, NCS also # imports the contents of zephyr/west.yml at the above From 0f1c8a84062e6829e69f002f141eca1bc2a36253 Mon Sep 17 00:00:00 2001 From: Georgios Vasilakis Date: Tue, 30 Sep 2025 12:21:45 +0200 Subject: [PATCH 02/14] manifest: Update TF-M With updates which allows using system off from TF-M. Signed-off-by: Georgios Vasilakis --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index f930a5ecc84..7f73cc8c1d0 100644 --- a/west.yml +++ b/west.yml @@ -147,7 +147,7 @@ manifest: - name: trusted-firmware-m repo-path: sdk-trusted-firmware-m path: modules/tee/tf-m/trusted-firmware-m - revision: cc5a59a410adc66d3b1b34a24efe00a7ac2fcf5b + revision: df48857af53909c91e02d75d84e46ce3a37f10b4 - name: psa-arch-tests repo-path: sdk-psa-arch-tests path: modules/tee/tf-m/psa-arch-tests From 9ca7b88dabe046cd7ae43084b1b4fe5fd25dacbb Mon Sep 17 00:00:00 2001 From: Ravi Dondaputi Date: Fri, 31 Oct 2025 14:57:11 +0530 Subject: [PATCH 03/14] manifest: sdk-nrfxlib: Update firmware patch blobs Pull in changes done to support WPS connection and allowing probe request to be sent to supplicant. Signed-off-by: Ravi Dondaputi --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index 7f73cc8c1d0..af7e5f696c1 100644 --- a/west.yml +++ b/west.yml @@ -143,7 +143,7 @@ manifest: - name: nrfxlib repo-path: sdk-nrfxlib path: nrfxlib - revision: 9b969cc34b07d44c8b3ad5bb6fa5cab6646e7ed1 + revision: 3fcb5500a4ba94b5bb89f1552726b93cc7f96b7a - name: trusted-firmware-m repo-path: sdk-trusted-firmware-m path: modules/tee/tf-m/trusted-firmware-m From 1294c09beb6ba51f7d03064e9f59ca36873001d4 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Fri, 28 Nov 2025 11:54:35 +0100 Subject: [PATCH 04/14] lib: nrf_modem_lib: add support for TLS_DTLS_FRAG_EXT This socket option is used to enable and disable the DTLS fragmentation extension. Signed-off-by: Robert Lubos Signed-off-by: Kacper Radoszewski --- lib/nrf_modem_lib/nrf9x_sockets.c | 3 ++ .../nrf9x_sockets/src/nrf9x_sockets_test.c | 47 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/lib/nrf_modem_lib/nrf9x_sockets.c b/lib/nrf_modem_lib/nrf9x_sockets.c index 6eb7e5442e1..6374a6efb81 100644 --- a/lib/nrf_modem_lib/nrf9x_sockets.c +++ b/lib/nrf_modem_lib/nrf9x_sockets.c @@ -203,6 +203,9 @@ static int z_to_nrf_optname(int z_in_level, int z_in_optname, case TLS_DTLS_HANDSHAKE_STATUS: *nrf_out_optname = NRF_SO_SEC_HANDSHAKE_STATUS; break; + case TLS_DTLS_FRAG_EXT: + *nrf_out_optname = NRF_SO_SEC_DTLS_FRAG_EXT; + break; default: retval = -1; break; diff --git a/tests/lib/nrf_modem_lib/nrf9x_sockets/src/nrf9x_sockets_test.c b/tests/lib/nrf_modem_lib/nrf9x_sockets/src/nrf9x_sockets_test.c index 8ebcaee4b54..b9c223060d2 100644 --- a/tests/lib/nrf_modem_lib/nrf9x_sockets/src/nrf9x_sockets_test.c +++ b/tests/lib/nrf_modem_lib/nrf9x_sockets/src/nrf9x_sockets_test.c @@ -1866,6 +1866,53 @@ void test_nrf9x_socket_offload_sendcb(void) ret = zsock_sendto(fd, data, data_len, flags, &to, 42); TEST_ASSERT_EQUAL(8, ret); TEST_ASSERT_EQUAL(1, sendcb_calls); + + __cmock_nrf_close_ExpectAndReturn(nrf_fd, 0); + + ret = zsock_close(fd); + + TEST_ASSERT_EQUAL(ret, 0); +} + +void test_nrf9x_socket_offload_dtls_frag_ext(void) +{ + int ret; + int fd; + int nrf_fd = NRF_FD; + int family = AF_INET; + int type = SOCK_STREAM; + int proto = IPPROTO_TCP; + int data = 0; + size_t data_len = sizeof(data); + + __cmock_nrf_socket_ExpectAndReturn(NRF_AF_INET, NRF_SOCK_STREAM, NRF_IPPROTO_TCP, nrf_fd); + + fd = zsock_socket(family, type, proto); + + TEST_ASSERT_EQUAL(fd, 0); + + __cmock_nrf_setsockopt_ExpectAndReturn(nrf_fd, NRF_SOL_SECURE, NRF_SO_SEC_DTLS_FRAG_EXT, + NULL, sizeof(int), 0); + __cmock_nrf_setsockopt_IgnoreArg_option_value(); + + ret = zsock_setsockopt(fd, SOL_TLS, TLS_DTLS_FRAG_EXT, &data, sizeof(data)); + + TEST_ASSERT_EQUAL(ret, 0); + + __cmock_nrf_getsockopt_ExpectAndReturn(nrf_fd, NRF_SOL_SECURE, NRF_SO_SEC_DTLS_FRAG_EXT, + NULL, NULL, 0); + __cmock_nrf_getsockopt_IgnoreArg_option_value(); + __cmock_nrf_getsockopt_IgnoreArg_option_len(); + + ret = zsock_getsockopt(fd, SOL_TLS, TLS_DTLS_FRAG_EXT, &data, &data_len); + + TEST_ASSERT_EQUAL(ret, 0); + + __cmock_nrf_close_ExpectAndReturn(nrf_fd, 0); + + ret = zsock_close(fd); + + TEST_ASSERT_EQUAL(ret, 0); } /* It is required to be added to each test. That is because unity's From 0915a0063d750f80ddd0d1dc5c92e0395de1f5c3 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 27 Nov 2025 01:30:43 +0530 Subject: [PATCH 05/14] boards: shields: nrf7002eb2: Remove from downstream Use the upstream shield directly. Signed-off-by: Chaitanya Tata --- boards/shields/nrf7002eb2/Kconfig.shield | 14 --- .../boards/nrf54l15dk_nrf54l15_cpuapp.overlay | 54 ----------- .../nrf54lm20dk_nrf54lm20a_cpuapp.overlay | 89 ------------------- boards/shields/nrf7002eb2/nrf7002eb2.overlay | 24 ----- .../nrf7002eb2/nrf7002eb2_coex.overlay | 15 ---- .../shields/nrf7002eb2/nrf7002eb2_common.dtsi | 20 ----- .../nrf7002eb2/nrf7002eb2_common_5g.dtsi | 12 --- .../nrf7002eb2/nrf7002eb2_gpio_pins_1.dtsi | 25 ------ .../nrf7002eb2/nrf7002eb2_gpio_pins_2.dtsi | 15 ---- .../nrf7002eb2/nrf7002eb2_nrf7000.overlay | 24 ----- .../nrf7002eb2/nrf7002eb2_nrf7001.overlay | 23 ----- 11 files changed, 315 deletions(-) delete mode 100644 boards/shields/nrf7002eb2/Kconfig.shield delete mode 100644 boards/shields/nrf7002eb2/boards/nrf54l15dk_nrf54l15_cpuapp.overlay delete mode 100644 boards/shields/nrf7002eb2/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2.overlay delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_coex.overlay delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_common.dtsi delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_common_5g.dtsi delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_1.dtsi delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_2.dtsi delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_nrf7000.overlay delete mode 100644 boards/shields/nrf7002eb2/nrf7002eb2_nrf7001.overlay diff --git a/boards/shields/nrf7002eb2/Kconfig.shield b/boards/shields/nrf7002eb2/Kconfig.shield deleted file mode 100644 index 428fcb5ace2..00000000000 --- a/boards/shields/nrf7002eb2/Kconfig.shield +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2025 Nordic Semiconductor ASA -# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - -config SHIELD_NRF7002EB2 - def_bool $(shields_list_contains,nrf7002eb2) - -config SHIELD_NRF7002EB2_NRF7001 - def_bool $(shields_list_contains,nrf7002eb2_nrf7001) - -config SHIELD_NRF7002EB2_NRF7000 - def_bool $(shields_list_contains,nrf7002eb2_nrf7000) - -config SHIELD_NRF7002EB2_COEX - def_bool $(shields_list_contains,nrf7002eb2_coex) diff --git a/boards/shields/nrf7002eb2/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/boards/shields/nrf7002eb2/boards/nrf54l15dk_nrf54l15_cpuapp.overlay deleted file mode 100644 index aeba7b96789..00000000000 --- a/boards/shields/nrf7002eb2/boards/nrf54l15dk_nrf54l15_cpuapp.overlay +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include "../nrf7002eb2_gpio_pins_1.dtsi" - -/ { - chosen { - zephyr,wifi = &wlan0; - zephyr,console = &uart30; - zephyr,shell-uart = &uart30; - zephyr,uart-mcumgr = &uart30; - zephyr,bt-mon-uart = &uart30; - zephyr,bt-c2h-uart = &uart30; - }; -}; - -&pinctrl { - spi22_default: spi22_default { - group1 { - psels = , - , - ; - bias-pull-down; - }; - }; - - spi22_sleep: spi22_sleep { - group1 { - psels = , - , - ; - bias-pull-down; - low-power-enable; - }; - }; -}; - -&spi22 { - cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&spi22_default>; - pinctrl-1 = <&spi22_sleep>; - pinctrl-names = "default", "sleep"; -}; - -&uart20 { - status = "disabled"; -}; - -&uart30 { - status = "okay"; -}; diff --git a/boards/shields/nrf7002eb2/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay b/boards/shields/nrf7002eb2/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay deleted file mode 100644 index 780c7911140..00000000000 --- a/boards/shields/nrf7002eb2/boards/nrf54lm20dk_nrf54lm20a_cpuapp.overlay +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#include "../nrf7002eb2_gpio_pins_2.dtsi" - -/ { - chosen { - zephyr,wifi = &wlan0; - zephyr,console = &uart30; - zephyr,shell-uart = &uart30; - zephyr,uart-mcumgr = &uart30; - zephyr,bt-mon-uart = &uart30; - zephyr,bt-c2h-uart = &uart30; - }; - - buttons { - /delete-node/ button_3; - }; - - aliases { - /delete-property/ sw3; - }; -}; - -&gpio3 { - status = "okay"; -}; - -&pinctrl { - spi22_default: spi22_default { - group1 { - psels = , - , - ; - bias-pull-down; - }; - }; - - spi22_sleep: spi22_sleep { - group1 { - psels = , - , - ; - bias-pull-down; - low-power-enable; - }; - }; - - uart30_default: uart30_default { - group1 { - psels = ; - }; - - group2 { - psels = ; - bias-pull-up; - }; - }; - - uart30_sleep: uart30_sleep { - group1 { - psels = , - ; - low-power-enable; - }; - }; -}; - -&spi22 { - status = "okay"; - cs-gpios = <&gpio3 2 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&spi22_default>; - pinctrl-1 = <&spi22_sleep>; - pinctrl-names = "default", "sleep"; -}; - -/* uart20 has pin conflicts with EB-II shield hence disabling that - * and enabling uart30 as console port. - */ -&uart20 { - status = "disabled"; -}; - -&uart30 { - status = "okay"; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2.overlay b/boards/shields/nrf7002eb2/nrf7002eb2.overlay deleted file mode 100644 index f81ae47a267..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2.overlay +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - zephyr,wifi = &wlan0; - }; -}; - -&wifi_spi { - status = "okay"; - - nrf70: nrf7002-spi@0 { - compatible = "nordic,nrf7002-spi"; - status = "okay"; - - /* Include common nRF70 overlays */ - #include "nrf7002eb2_common.dtsi" - #include "nrf7002eb2_common_5g.dtsi" - }; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_coex.overlay b/boards/shields/nrf7002eb2/nrf7002eb2_coex.overlay deleted file mode 100644 index 0e13d49eb68..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_coex.overlay +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - nrf_radio_coex: coex { - compatible = "nordic,nrf7002-coex"; - status = "okay"; - status0-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>; - req-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>; - grant-gpios = <&gpio1 7 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>; - }; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_common.dtsi b/boards/shields/nrf7002eb2/nrf7002eb2_common.dtsi deleted file mode 100644 index 4bd3c541079..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_common.dtsi +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ -#include - -/* Common assignments for nRF70 EB-II shield */ -reg = <0>; -spi-max-frequency = ; - -/* Maximum TX power limits for 2.4 GHz */ -wifi-max-tx-pwr-2g-dsss = <21>; -wifi-max-tx-pwr-2g-mcs0 = <16>; -wifi-max-tx-pwr-2g-mcs7 = <16>; - -/* List of interfaces */ -wlan0: wlan0 { - compatible = "nordic,wlan"; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_common_5g.dtsi b/boards/shields/nrf7002eb2/nrf7002eb2_common_5g.dtsi deleted file mode 100644 index fb9dcdde0dd..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_common_5g.dtsi +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -wifi-max-tx-pwr-5g-low-mcs0 = <13>; -wifi-max-tx-pwr-5g-low-mcs7 = <13>; -wifi-max-tx-pwr-5g-mid-mcs0 = <13>; -wifi-max-tx-pwr-5g-mid-mcs7 = <13>; -wifi-max-tx-pwr-5g-high-mcs0 = <12>; -wifi-max-tx-pwr-5g-high-mcs7 = <12>; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_1.dtsi b/boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_1.dtsi deleted file mode 100644 index 509a5ec9652..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_1.dtsi +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - nrf_radio_coex: coex { - compatible = "nordic,nrf7002-coex"; - status = "disabled"; - status0-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; - req-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; - grant-gpios = <&gpio1 12 (GPIO_PULL_DOWN | GPIO_ACTIVE_LOW)>; - }; -}; - -&nrf70 { - iovdd-ctrl-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; - bucken-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; - host-irq-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>; -}; - -&gpio1 { - status = "okay"; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_2.dtsi b/boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_2.dtsi deleted file mode 100644 index c5235b13af9..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_gpio_pins_2.dtsi +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -&nrf70 { - iovdd-ctrl-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; - bucken-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; - host-irq-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; -}; - -&gpio1 { - status = "okay"; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_nrf7000.overlay b/boards/shields/nrf7002eb2/nrf7002eb2_nrf7000.overlay deleted file mode 100644 index 306f2c46627..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_nrf7000.overlay +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - zephyr,wifi = &wlan0; - }; -}; - -&wifi_spi { - status = "okay"; - - nrf70: nrf7000-spi@0 { - compatible = "nordic,nrf7000-spi"; - status = "okay"; - - /* Include common nRF70 overlays */ - #include "nrf7002eb2_common.dtsi" - #include "nrf7002eb2_common_5g.dtsi" - }; -}; diff --git a/boards/shields/nrf7002eb2/nrf7002eb2_nrf7001.overlay b/boards/shields/nrf7002eb2/nrf7002eb2_nrf7001.overlay deleted file mode 100644 index bc2bb8b0d04..00000000000 --- a/boards/shields/nrf7002eb2/nrf7002eb2_nrf7001.overlay +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2025 Nordic Semiconductor - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -/ { - chosen { - zephyr,wifi = &wlan0; - }; -}; - -&wifi_spi { - status = "okay"; - - nrf70: nrf7001-spi@0 { - compatible = "nordic,nrf7001-spi"; - status = "okay"; - - /* Include common nRF70 overlays */ - #include "nrf7002eb2_common.dtsi" - }; -}; From 07010db00b98e88a51a388506d7a7a4e3adc0c5c Mon Sep 17 00:00:00 2001 From: Georgios Vasilakis Date: Tue, 30 Sep 2025 12:19:10 +0200 Subject: [PATCH 06/14] modules: TF-M: Add TF-M HAL function to enable system off Add the tfm_platform_hal_system_off function which puts the device in system off. This only needed for nRF54L devices at the moment and only in TF-M SFN mode. Signed-off-by: Georgios Vasilakis --- modules/trusted-firmware-m/CMakeLists.txt | 1 + modules/trusted-firmware-m/Kconfig | 14 ++++++++++++++ .../tfm_boards/src/tfm_platform_system.c | 17 +++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt index 437de0371dd..aca446c5ad7 100644 --- a/modules/trusted-firmware-m/CMakeLists.txt +++ b/modules/trusted-firmware-m/CMakeLists.txt @@ -103,6 +103,7 @@ set_property(TARGET zephyr_property_target $<$:-DCONFIG_NRF_SECURE_APPROTECT_USER_HANDLING=ON> $<$:-DCONFIG_IDENTITY_KEY_TFM=ON> $<$:-DPS_SUPPORT_FORMAT_TRANSITION=ON> + $<$:-DTFM_NRF_SYSTEM_OFF_SERVICE=ON> ) if(CONFIG_TFM_PROFILE_TYPE_MINIMAL) diff --git a/modules/trusted-firmware-m/Kconfig b/modules/trusted-firmware-m/Kconfig index 1b6271d7949..78bdd98287d 100644 --- a/modules/trusted-firmware-m/Kconfig +++ b/modules/trusted-firmware-m/Kconfig @@ -538,4 +538,18 @@ config TFM_PSA_FRAMEWORK_HAS_MM_IOVEC Memory-mapped iovecs provide direct mapping of client input and output vectors into the Secure Partition. +config TFM_NRF_SYSTEM_OFF_SERVICE + bool "TF-M NRF System Off Service [EXPERIMENTAL]" + depends on TFM_ISOLATION_LEVEL = 1 + depends on TFM_SFN + depends on SOC_SERIES_NRF54LX + depends on !RETAINED_MEM_NRF_RAM_CTRL + select EXPERIMENTAL + help + Provide a system off service for the nRF54L series SoCs. + This service allows the non-secure application to request + the system to enter system off mode via a secure service call. + This service will disable RAM retention for all RAM blocks + before entering system off mode. + endif # BUILD_WITH_TFM diff --git a/modules/trusted-firmware-m/tfm_boards/src/tfm_platform_system.c b/modules/trusted-firmware-m/tfm_boards/src/tfm_platform_system.c index b9953e7575f..ab452290752 100644 --- a/modules/trusted-firmware-m/tfm_boards/src/tfm_platform_system.c +++ b/modules/trusted-firmware-m/tfm_boards/src/tfm_platform_system.c @@ -16,6 +16,9 @@ #include #include +#include +#include + #include "handle_attr.h" #if NRF_ALLOW_NON_SECURE_FAULT_HANDLING @@ -28,6 +31,20 @@ void tfm_platform_hal_system_reset(void) NVIC_SystemReset(); } +#if TFM_NRF_SYSTEM_OFF_SERVICE +enum tfm_platform_err_t tfm_platform_hal_system_off(void) +{ + __disable_irq(); + + nrfx_ram_ctrl_retention_enable_all_set(false); + + nrf_regulators_system_off(NRF_REGULATORS); + + /* This should be unreachable */ + return TFM_PLATFORM_ERR_SYSTEM_ERROR; +} +#endif /* TFM_NRF_SYSTEM_OFF_SERVICE */ + #if CONFIG_FW_INFO static enum tfm_platform_err_t tfm_platform_hal_fw_info_service(psa_invec *in_vec, psa_outvec *out_vec) From 67a7d8c6a277e1e49ab49331b527bbc60d7ae761 Mon Sep 17 00:00:00 2001 From: Georgios Vasilakis Date: Tue, 25 Nov 2025 16:12:41 +0100 Subject: [PATCH 07/14] samples: system_off: Add overlays for the nRF54L devices Add an overlay for the nRF54L devices which support TF-M with system off. This is needed because the feature is not enabled by default and it also have requirements which deviate from the default TF-M configuration. Signed-off-by: Georgios Vasilakis --- .../nordic/system_off/boards/nrf54l15dk_nrf54l10_cpuapp_ns.conf | 2 ++ .../nordic/system_off/boards/nrf54l15dk_nrf54l15_cpuapp_ns.conf | 2 ++ .../system_off/boards/nrf54lm20dk_nrf54lm20a_cpuapp_ns.conf | 2 ++ .../system_off/boards/nrf54lv10dk_nrf54lv10a_cpuapp_ns.conf | 2 ++ 4 files changed, 8 insertions(+) create mode 100644 samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l10_cpuapp_ns.conf create mode 100644 samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l15_cpuapp_ns.conf create mode 100644 samples/zephyr/boards/nordic/system_off/boards/nrf54lm20dk_nrf54lm20a_cpuapp_ns.conf create mode 100644 samples/zephyr/boards/nordic/system_off/boards/nrf54lv10dk_nrf54lv10a_cpuapp_ns.conf diff --git a/samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l10_cpuapp_ns.conf b/samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l10_cpuapp_ns.conf new file mode 100644 index 00000000000..6cd2bff5722 --- /dev/null +++ b/samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l10_cpuapp_ns.conf @@ -0,0 +1,2 @@ +CONFIG_TFM_SFN=y +CONFIG_TFM_NRF_SYSTEM_OFF_SERVICE=y diff --git a/samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l15_cpuapp_ns.conf b/samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l15_cpuapp_ns.conf new file mode 100644 index 00000000000..6cd2bff5722 --- /dev/null +++ b/samples/zephyr/boards/nordic/system_off/boards/nrf54l15dk_nrf54l15_cpuapp_ns.conf @@ -0,0 +1,2 @@ +CONFIG_TFM_SFN=y +CONFIG_TFM_NRF_SYSTEM_OFF_SERVICE=y diff --git a/samples/zephyr/boards/nordic/system_off/boards/nrf54lm20dk_nrf54lm20a_cpuapp_ns.conf b/samples/zephyr/boards/nordic/system_off/boards/nrf54lm20dk_nrf54lm20a_cpuapp_ns.conf new file mode 100644 index 00000000000..6cd2bff5722 --- /dev/null +++ b/samples/zephyr/boards/nordic/system_off/boards/nrf54lm20dk_nrf54lm20a_cpuapp_ns.conf @@ -0,0 +1,2 @@ +CONFIG_TFM_SFN=y +CONFIG_TFM_NRF_SYSTEM_OFF_SERVICE=y diff --git a/samples/zephyr/boards/nordic/system_off/boards/nrf54lv10dk_nrf54lv10a_cpuapp_ns.conf b/samples/zephyr/boards/nordic/system_off/boards/nrf54lv10dk_nrf54lv10a_cpuapp_ns.conf new file mode 100644 index 00000000000..6cd2bff5722 --- /dev/null +++ b/samples/zephyr/boards/nordic/system_off/boards/nrf54lv10dk_nrf54lv10a_cpuapp_ns.conf @@ -0,0 +1,2 @@ +CONFIG_TFM_SFN=y +CONFIG_TFM_NRF_SYSTEM_OFF_SERVICE=y From ad78111e5b61392085631c7a620039c7a50bee0c Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Thu, 20 Nov 2025 15:15:18 +0100 Subject: [PATCH 08/14] img_mgmt: Use absolute address while checking app Use absolute addresses while determining a running application partition. Ref: NCSIDB-1773 Signed-off-by: Tomasz Chyrowicz --- include/flash_map_pm.h | 10 ++++++++++ subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/include/flash_map_pm.h b/include/flash_map_pm.h index 76c8215953a..6074c48d916 100644 --- a/include/flash_map_pm.h +++ b/include/flash_map_pm.h @@ -68,6 +68,16 @@ (DEVICE_DT_GET_OR_NULL(DT_NODELABEL(FIXED_PARTITION_DATA_FIELD(label, _DEV))))) #define FLASH_AREA_DEVICE(label) FIXED_PARTITION_DEVICE(label) +#define FIXED_PARTITION_MTD(label) \ + COND_CODE_1(DT_NODE_EXISTS(FIXED_PARTITION_DATA_FIELD(label, _DEV)), \ + (FIXED_PARTITION_DATA_FIELD(label, _DEV)), \ + (DT_NODELABEL(FIXED_PARTITION_DATA_FIELD(label, _DEV)))) +#define FIXED_PARTITION_NODE_MTD(node) \ + COND_CODE_1( \ + DT_FIXED_SUBPARTITION_EXISTS(node), \ + (DT_MTD_FROM_FIXED_SUBPARTITION(node)), \ + (DT_MTD_FROM_FIXED_PARTITION(node))) + #define FIXED_PARTITION_EXISTS(label) IS_ENABLED(PM_IS_ENABLED(label)) #define FLASH_AREA_LABEL_EXISTS(label) FIXED_PARTITION_EXISTS(label) diff --git a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c index 46b39bb4479..1990a465b2d 100644 --- a/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c +++ b/subsys/mgmt/mcumgr/grp/img_mgmt/src/img_mgmt.c @@ -71,6 +71,8 @@ BUILD_ASSERT(PM_MCUBOOT_PAD_SIZE == PM_MCUBOOT_SECONDARY_PAD_SIZE); #endif #define FIXED_PARTITION_IS_RUNNING_APP_PARTITION(label) \ + DT_SAME_NODE(FIXED_PARTITION_NODE_MTD(DT_CHOSEN(zephyr_code_partition)), \ + FIXED_PARTITION_MTD(label)) && \ (FIXED_PARTITION_OFFSET(label) <= CONFIG_FLASH_LOAD_OFFSET && \ FIXED_PARTITION_OFFSET(label) + FIXED_PARTITION_SIZE(label) > CONFIG_FLASH_LOAD_OFFSET) #endif /* USE_PARTITION_MANAGER */ From 600888b33b26634ae1714341fb57198753abe6b3 Mon Sep 17 00:00:00 2001 From: Ravi Dondaputi Date: Fri, 31 Oct 2025 15:30:41 +0530 Subject: [PATCH 09/14] snippets: Add snippet for P2P build options Create snippet for P2P build. Signed-off-by: Ravi Dondaputi --- snippets/wifi-p2p/snippet.yml | 3 +++ snippets/wifi-p2p/wifi-p2p.conf | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 snippets/wifi-p2p/snippet.yml create mode 100644 snippets/wifi-p2p/wifi-p2p.conf diff --git a/snippets/wifi-p2p/snippet.yml b/snippets/wifi-p2p/snippet.yml new file mode 100644 index 00000000000..0b1ac7ab651 --- /dev/null +++ b/snippets/wifi-p2p/snippet.yml @@ -0,0 +1,3 @@ +name: wifi-p2p +append: + EXTRA_CONF_FILE: wifi-p2p.conf diff --git a/snippets/wifi-p2p/wifi-p2p.conf b/snippets/wifi-p2p/wifi-p2p.conf new file mode 100644 index 00000000000..a7a4cf6764a --- /dev/null +++ b/snippets/wifi-p2p/wifi-p2p.conf @@ -0,0 +1,7 @@ +CONFIG_NRF70_P2P_MODE=y +CONFIG_NRF70_AP_MODE=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_P2P=y +CONFIG_WPA_CLI=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LOG_LEVEL_INF=y +CONFIG_LTO=y +CONFIG_ISR_TABLES_LOCAL_DECLARATION=y From bf70ac3175282ae9309d26fc8ded98d06b87a166 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 24 Jul 2025 21:17:02 +0530 Subject: [PATCH 10/14] net: lib: hostap_crypto: Add support for WPA3-PSA This implements the PSA variant of WPA3, disables the internal WPA3 in the supplicant and uses the nRF Oberon's WPA3 HKDF APIs. Signed-off-by: Chaitanya Tata --- .../nrf70/wifi_advanced_security_modes.rst | 2 +- subsys/net/lib/hostap_crypto/CMakeLists.txt | 10 + subsys/net/lib/hostap_crypto/Kconfig | 20 +- subsys/net/lib/hostap_crypto/wpa3_psa.c | 1418 +++++++++++++++++ 4 files changed, 1442 insertions(+), 8 deletions(-) create mode 100644 subsys/net/lib/hostap_crypto/wpa3_psa.c diff --git a/doc/nrf/app_dev/device_guides/nrf70/wifi_advanced_security_modes.rst b/doc/nrf/app_dev/device_guides/nrf70/wifi_advanced_security_modes.rst index 841797b71d9..76b8719566d 100644 --- a/doc/nrf/app_dev/device_guides/nrf70/wifi_advanced_security_modes.rst +++ b/doc/nrf/app_dev/device_guides/nrf70/wifi_advanced_security_modes.rst @@ -220,7 +220,7 @@ This improves the security of the nRF70 device compared to the non-PSA mode. .. note:: - Currently, the PSA crypto support is only applicable to the WPA2™-personal security profile. + Currently, the PSA crypto support is only applicable to the WPA2™ and WPA3™-personal security profiles. Enable PSA support ================== diff --git a/subsys/net/lib/hostap_crypto/CMakeLists.txt b/subsys/net/lib/hostap_crypto/CMakeLists.txt index fcb299db744..8cf15f39ee3 100644 --- a/subsys/net/lib/hostap_crypto/CMakeLists.txt +++ b/subsys/net/lib/hostap_crypto/CMakeLists.txt @@ -79,4 +79,14 @@ if(DEFINED CONFIG_HOSTAP_CRYPTO_ALT_PSA) ${HOSTAP_BASE}/port/mbedtls/supp_psa_api.c ${HOSTAP_SRC_BASE}/crypto/tls_none.c ) + + if(CONFIG_HOSTAP_CRYPTO_WPA3_PSA) + zephyr_library_sources( + wpa3_psa.c + ) + zephyr_library_compile_definitions( + CONFIG_SAE + CONFIG_ECC + ) + endif() endif() diff --git a/subsys/net/lib/hostap_crypto/Kconfig b/subsys/net/lib/hostap_crypto/Kconfig index 0cb3eb5e55c..8744ab65c6c 100644 --- a/subsys/net/lib/hostap_crypto/Kconfig +++ b/subsys/net/lib/hostap_crypto/Kconfig @@ -97,15 +97,21 @@ config HOSTAP_CRYPTO_ENTERPRISE endif -# PSA crypto is WPA2 only for now +# PSA crypto is personal security only for now if HOSTAP_CRYPTO_ALT_PSA + # PSA doesn't work with WPA3 builtin (uses bignum) + config WIFI_NM_WPA_SUPPLICANT_WPA3 + default n + # PSA doesn't support with enterprise mode yet + config WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE + default n -config WIFI_NM_WPA_SUPPLICANT_WPA3 - default n - -config WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE - default n - + config HOSTAP_CRYPTO_WPA3_PSA + bool "WPA3 PSA support" + select EXPERIMENTAL + select PSA_WANT_ALG_WPA3_SAE + select PSA_WANT_ALG_WPA3_SAE_H2E + select PSA_WANT_KEY_TYPE_WPA3_SAE_PT endif endif diff --git a/subsys/net/lib/hostap_crypto/wpa3_psa.c b/subsys/net/lib/hostap_crypto/wpa3_psa.c new file mode 100644 index 00000000000..d9402fcb4c1 --- /dev/null +++ b/subsys/net/lib/hostap_crypto/wpa3_psa.c @@ -0,0 +1,1418 @@ +/* + * WPA3-SAE implementation using PSA APIs + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + * + * This implementation provides a drop-in replacement for the original SAE + * implementation using PSA PAKE APIs. It supports: + * - Group 19 (NIST P-256) only + * - H2E (Hash-to-Element) support + * - HNP (Hash-and-Password) support + * - No GDH (Group-Dependent Hash) support + * - Exact same API as original SAE implementation + */ + +#include "includes.h" +#include "common.h" +#include "common/defs.h" +#include "common/wpa_common.h" +#include "utils/const_time.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "ieee802_11_defs.h" +#include "sae.h" + +#include "psa/crypto.h" +#include "psa/crypto_extra.h" +#include "mbedtls/md.h" +#include +/* WPA3-SAE PSA implementation constants */ +#define WPA3_PSA_MAX_COMMIT_LEN 1024 +#define WPA3_PSA_MAX_CONFIRM_LEN 1024 + +/* Setup operation parameters */ +struct wpa3_psa_setup_params { + const u8 *addr1; + const u8 *addr2; + const u8 *password; + size_t password_len; + const u8 *ssid; + size_t ssid_len; + int group; + bool h2e; + bool gdh; +}; + +/* Internal PSA operation context */ +struct wpa3_psa_operation { + psa_pake_operation_t pake_op; + psa_pake_cipher_suite_t cipher_suite; + psa_key_id_t password_key; + enum sae_state state; + u8 own_addr[ETH_ALEN]; + u8 peer_addr[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u8 password[256]; + size_t password_len; + u8 shared_key[256]; /* Increased for PSA PAKE commit/confirm data */ + size_t shared_key_len; + u8 local_commit[256]; /* Store local commit for context calculation */ + size_t local_commit_len; + u8 peer_commit[256]; /* Store peer commit for context calculation */ + size_t peer_commit_len; + u8 pmk[SAE_PMK_LEN_MAX]; + size_t pmk_len; + u8 kck[SAE_KCK_LEN]; + u8 pmkid[SAE_PMKID_LEN]; + int group; + psa_algorithm_t hash_alg; + bool h2e_enabled; + bool gdh_enabled; + u16 send_confirm; + u16 rc; +}; + +/* Forward declarations */ + +static int wpa3_psa_setup_operation(struct wpa3_psa_operation *op, + const struct wpa3_psa_setup_params *params); +static int wpa3_psa_derive_commit(struct wpa3_psa_operation *op); +static int wpa3_psa_process_commit(struct wpa3_psa_operation *op, const u8 *commit_data, + size_t commit_len); +static int wpa3_psa_derive_confirm(struct wpa3_psa_operation *op); +static int wpa3_psa_process_confirm(struct wpa3_psa_operation *op, const u8 *confirm_data, + size_t confirm_len); +static int wpa3_psa_derive_keys(struct wpa3_psa_operation *op); +static void wpa3_psa_cleanup_operation(struct wpa3_psa_operation *op); +static struct sae_pt *wpa3_psa_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, + const u8 *password, size_t password_len, + const char *identifier); +/* Map sae_data to internal PSA operation */ +static struct wpa3_psa_operation *get_psa_op(struct sae_data *sae) +{ + if (!sae) { + wpa_printf(MSG_ERROR, "WPA3-PSA: get_psa_op failed - sae is NULL"); + return NULL; + } + + if (!sae->tmp) { + wpa_printf(MSG_DEBUG, + "WPA3-PSA: get_psa_op - sae->tmp is NULL (expected during cleanup)"); + return NULL; + } + + return (struct wpa3_psa_operation *)sae->tmp; +} + +/* Initialize PSA operation in sae_data */ +static int init_psa_op(struct sae_data *sae) +{ + struct wpa3_psa_operation *op; + + if (!sae) { + wpa_printf(MSG_ERROR, "WPA3-PSA: init_psa_op failed - sae is NULL"); + return -1; + } + + /* Check if operation already exists */ + if (sae->tmp) { + wpa3_psa_cleanup_operation((struct wpa3_psa_operation *)sae->tmp); + os_free(sae->tmp); + sae->tmp = NULL; + } + + /* Allocate PSA operation context */ + op = os_zalloc(sizeof(*op)); + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: init_psa_op failed - memory allocation failed"); + return -1; + } + + /* Initialize cipher suite first */ + op->cipher_suite = psa_pake_cipher_suite_init(); + + /* Initialize PSA PAKE operation */ + op->pake_op = psa_pake_operation_init(); + + /* Initialize operation fields */ + op->state = SAE_NOTHING; + op->password_key = PSA_KEY_ID_NULL; + op->send_confirm = 1; /* Initialize send_confirm counter to 1 (not 0) */ + op->shared_key_len = 0; + op->pmk_len = 0; + op->group = 19; /* Default to group 19 (NIST P-256) */ + op->h2e_enabled = false; + op->gdh_enabled = false; + + /* Clear sensitive data */ + os_memset(op->password, 0, sizeof(op->password)); + os_memset(op->shared_key, 0, sizeof(op->shared_key)); + os_memset(op->pmk, 0, sizeof(op->pmk)); + os_memset(op->kck, 0, sizeof(op->kck)); + os_memset(op->pmkid, 0, sizeof(op->pmkid)); + + /* Store in sae_data->tmp */ + sae->tmp = (struct sae_temporary_data *)op; + + return 0; +} + +/** + * sae_set_group - Set SAE group (exact same API as original) + * @sae: SAE data + * @group: DH group + * + * Returns: 0 on success, -1 on failure + */ +int sae_set_group(struct sae_data *sae, int group) +{ + struct wpa3_psa_operation *op; + + if (!sae) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_set_group failed - sae is NULL"); + return -1; + } + + /* Initialize PSA operation if not already done */ + if (!sae->tmp) { + if (init_psa_op(sae) != 0) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: sae_set_group failed - init_psa_op failed"); + return -1; + } + } + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_set_group failed - get_psa_op returned NULL"); + return -1; + } + + /* Handle group selection - group 0 means auto-select, default to group 19 (NIST P-256) */ + if (group == 0) { + group = 19; + } else if (group != 19) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Only group 19 (NIST P-256) is supported, got %d", + group); + return -1; + } + + op->group = group; + return 0; +} + +/** + * sae_clear_temp_data - Clear temporary SAE data (exact same API as original) + * @sae: SAE data + */ +void sae_clear_temp_data(struct sae_data *sae) +{ + struct wpa3_psa_operation *op; + + if (!sae) { + return; + } + + op = get_psa_op(sae); + if (op) { + wpa3_psa_cleanup_operation(op); + sae->tmp = NULL; + } +} + +/** + * sae_clear_data - Clear all SAE data (exact same API as original) + * @sae: SAE data + */ +void sae_clear_data(struct sae_data *sae) +{ + if (!sae) { + return; + } + + sae_clear_temp_data(sae); + + if (sae->tmp) { + os_free(sae->tmp); + sae->tmp = NULL; + } +} + +/** + * sae_prepare_commit - Prepare SAE commit message (exact same API as original) + * @addr1: First MAC address (typically own address) + * @addr2: Second MAC address (typically peer address) + * @password: Password for SAE + * @password_len: Length of password + * @sae: SAE data + * + * Returns: 0 on success, -1 on failure + */ +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, + struct sae_data *sae) +{ + struct wpa3_psa_operation *op; + int res; + + if (!sae) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_prepare_commit failed - sae is NULL"); + return -1; + } + + if (!addr1 || !addr2 || !password) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: sae_prepare_commit failed - invalid parameters (addr1=%p, " + "addr2=%p, password=%p)", + addr1, addr2, password); + return -1; + } + + /* Initialize PSA operation if not already done */ + if (init_psa_op(sae) < 0) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_prepare_commit failed - init_psa_op failed"); + return -1; + } + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: sae_prepare_commit failed - get_psa_op returned NULL"); + return -1; + } + + /* Store addresses and password for the full SAE flow */ + os_memcpy(op->own_addr, addr1, ETH_ALEN); + os_memcpy(op->peer_addr, addr2, ETH_ALEN); + os_memcpy(op->password, password, password_len); + op->password_len = password_len; + + /* Set up PSA operation for the full SAE flow */ + struct wpa3_psa_setup_params params = { + .addr1 = addr1, + .addr2 = addr2, + .password = password, + .password_len = password_len, + .ssid = op->ssid, + .ssid_len = op->ssid_len, + .group = op->group, + .h2e = op->h2e_enabled, + .gdh = op->gdh_enabled, + }; + res = wpa3_psa_setup_operation(op, ¶ms); + if (res < 0) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_prepare_commit failed - setup failed"); + return -1; + } + + /* Derive commit (M1 message) */ + res = wpa3_psa_derive_commit(op); + if (res < 0) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_prepare_commit failed - derive failed"); + return -1; + } + + /* Set state to committed - ready to send M1 */ + sae->state = SAE_COMMITTED; + + return 0; +} + +/** + * sae_write_commit - Write SAE commit message to buffer (exact same API as original) + * @sae: SAE data + * @buf: Buffer to write commit message + * @token: Token + * @identifier: Identifier + * + * Returns: 0 on success, -1 on failure + */ +int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, const struct wpabuf *token, + const char *identifier) +{ + struct wpa3_psa_operation *op; + u8 *pos; + + op = get_psa_op(sae); + if (!op || sae->state != SAE_COMMITTED) { + return -1; + } + + /* Get commit data from PSA operation */ + if (op->shared_key_len == 0) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: No commit data available"); + return -1; + } + + /* PSA PAKE output should be exactly 98 bytes in SAE format: [group][scalar][element] */ + if (op->shared_key_len != 98) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Commit data wrong size (%zu bytes, expected 98)", + op->shared_key_len); + return -1; + } + + /* Use PSA PAKE output directly as commit message */ + pos = wpabuf_put(buf, op->shared_key_len); + os_memcpy(pos, op->shared_key, op->shared_key_len); + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Commit message written (%zu bytes)", wpabuf_len(buf)); + wpa_hexdump(MSG_DEBUG, "WPA3-PSA: Full commit message", pos, op->shared_key_len); + return 0; +} + +/** + * sae_parse_commit - Parse and process received commit message (exact same API as original) + * @sae: SAE data + * @data: Commit message data + * @len: Length of commit message + * @token: Token + * @token_len: Token length + * @allowed_groups: Allowed groups + * @h2e: H2E flag + * @ie_offset: IE offset + * + * Returns: 0 on success, SAE_SILENTLY_DISCARD on discard, -1 on failure + */ +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, + size_t *token_len, int *allowed_groups, int h2e, int *ie_offset) +{ + struct wpa3_psa_operation *op; + u16 res = WLAN_STATUS_SUCCESS; + + op = get_psa_op(sae); + if (!op) { + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* SAE commit message should be exactly 98 bytes: [group][scalar][element] */ + if (len != 98) { + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* Store the full commit message for PSA processing */ + os_memcpy(op->shared_key, data, len); + op->shared_key_len = len; + + /* Also store for context calculation */ + os_memcpy(op->peer_commit, data, len); + op->peer_commit_len = len; + + /* Process the commit using PSA */ + res = wpa3_psa_process_commit(op, op->shared_key, op->shared_key_len); + if (res != WLAN_STATUS_SUCCESS) { + return res; + } + + /* Parse optional fields */ + if (token) { + *token = NULL; + *token_len = 0; + } + + if (allowed_groups) { + *allowed_groups = 0; /* No groups specified */ + } + + if (ie_offset) { + *ie_offset = len; /* No IE offset since we use the full message */ + } + + return WLAN_STATUS_SUCCESS; +} + +/** + * sae_write_confirm - Write SAE confirm message to buffer (exact same API as original) + * @sae: SAE data + * @buf: Buffer to write confirm message + * + * Returns: 0 on success, -1 on failure + */ +int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) +{ + struct wpa3_psa_operation *op; + u8 *pos; + + op = get_psa_op(sae); + if (!op || op->shared_key_len == 0) { + return -1; + } + + /* Format SAE confirm message */ + /* SAE confirm format: [confirm] - PSA confirm output already includes send_confirm */ + + /* Add confirm data - use the full PSA confirm output (includes send_confirm) */ + pos = wpabuf_put(buf, op->shared_key_len); + os_memcpy(pos, op->shared_key, op->shared_key_len); + + return 0; +} + +/** + * sae_check_confirm - Check SAE confirm message (exact same API as original) + * @sae: SAE data + * @data: Confirm message data + * @len: Length of confirm message + * @ie_offset: IE offset + * + * Returns: 0 on success, -1 on failure + */ +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len, int *ie_offset) +{ + struct wpa3_psa_operation *op; + const u8 *pos, *end; + size_t confirm_len; + int res; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Checking confirm message (M2)"); + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: No PSA operation available"); + return -1; + } + + pos = data; + end = data + len; + + /* Parse SAE confirm message format */ + /* Expected format: [confirm] - PSA confirm output already includes send_confirm */ + + /* Parse confirm data (includes send_confirm) */ + if (end - pos < 1) { /* Minimum confirm size */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Confirm message too short for confirm data"); + return -1; + } + + confirm_len = end - pos; + if (confirm_len > sizeof(op->shared_key)) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Confirm data too large"); + return -1; + } + + /* Process the confirm using PSA (includes send_confirm) */ + res = wpa3_psa_process_confirm(op, pos, confirm_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Failed to process confirm"); + return -1; + } + + /* Set state to accepted */ + sae->state = SAE_ACCEPTED; + + /* Populate the sae_data structure with the derived keys */ + /* This is what the supplicant expects to find in sae->pmk and sae->pmk_len */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Copying PMK to sae_data - op->pmk_len: %zu", op->pmk_len); + os_memcpy(sae->pmk, op->pmk, op->pmk_len); + sae->pmk_len = op->pmk_len; + os_memcpy(sae->pmkid, op->pmkid, SAE_PMKID_LEN); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Populated sae_data fields - pmk_len: %zu", sae->pmk_len); + + /* Set IE offset if requested */ + if (ie_offset) { + *ie_offset = pos - data; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Confirm message checked successfully"); + return 0; +} + +/** + * sae_group_allowed - Check if SAE group is allowed (exact same API as original) + * @sae: SAE data + * @allowed_groups: Allowed groups + * @group: Group to check + * + * Returns: 0 if allowed, -1 if not allowed + */ +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) +{ + /* Only allow group 19 */ + if (group == 19) { + return 0; + } + return -1; +} + +/** + * sae_state_txt - Get SAE state text (exact same API as original) + * @state: SAE state + * + * Returns: State text string + */ +const char *sae_state_txt(enum sae_state state) +{ + switch (state) { + case SAE_NOTHING: + return "NOTHING"; + case SAE_COMMITTED: + return "COMMITTED"; + case SAE_CONFIRMED: + return "CONFIRMED"; + case SAE_ACCEPTED: + return "ACCEPTED"; + default: + return "UNKNOWN"; + } +} + +/** + * sae_prepare_commit_pt - Prepare SAE commit message with PT (exact same API as original) + * @sae: SAE data + * @pt: SAE PT data + * @addr1: First MAC address + * @addr2: Second MAC address + * @rejected_groups: Rejected groups + * @pk: SAE PK data + * + * Returns: 0 on success, -1 on failure + */ +int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt, const u8 *addr1, + const u8 *addr2, int *rejected_groups, const struct sae_pk *pk) +{ + struct wpa3_psa_operation *op; + int res; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Prepare commit with PT (PSA mode)"); + + if (!sae || !pt) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Invalid parameters for PT commit"); + return -1; + } + + /* PSA PAKE handles PT internally, use standard SAE flow */ + + /* Initialize PSA operation if not already done */ + if (init_psa_op(sae) < 0) { + return -1; + } + + op = get_psa_op(sae); + if (!op) { + return -1; + } + + /* Store addresses for the SAE flow */ + os_memcpy(op->own_addr, addr1, ETH_ALEN); + os_memcpy(op->peer_addr, addr2, ETH_ALEN); + + /* Set up PSA operation for the full SAE flow with PT */ + /* For PSA, we don't need to do anything special with PT */ + struct wpa3_psa_setup_params params = { + .addr1 = addr1, + .addr2 = addr2, + .password = op->password, + .password_len = op->password_len, + .ssid = op->ssid, + .ssid_len = op->ssid_len, + .group = op->group, + .h2e = op->h2e_enabled, + .gdh = op->gdh_enabled, + }; + + res = wpa3_psa_setup_operation(op, ¶ms); + if (res < 0) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Failed to setup operation for PT commit"); + return -1; + } + + /* Derive commit (M1 message) */ + res = wpa3_psa_derive_commit(op); + if (res < 0) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Failed to derive commit with PT"); + return -1; + } + + /* Set state to committed - ready to send M1 */ + sae->state = SAE_COMMITTED; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: PT commit prepared successfully"); + return 0; +} + +/** + * sae_process_commit - Process SAE commit (exact same API as original) + * @sae: SAE data + * + * Returns: 0 on success, -1 on failure + */ +int sae_process_commit(struct sae_data *sae) +{ + struct wpa3_psa_operation *op; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: sae_process_commit called"); + + if (!sae) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_process_commit failed - sae is NULL"); + return -1; + } + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: sae_process_commit failed - get_psa_op returned NULL"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: sae_process_commit - operation: %p, state: %d", op, + op->state); + + if (op->state != SAE_COMMITTED) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: sae_process_commit failed - invalid state: %d (expected: %d)", + op->state, SAE_COMMITTED); + return -1; + } + + /* For PSA implementation, commit processing is done in sae_parse_commit */ + /* This function is called by the supplicant but we handle it in parse_commit */ + wpa_printf(MSG_DEBUG, + "WPA3-PSA: sae_process_commit - commit processing already done in parse_commit"); + return 0; +} + +/** + * sae_derive_pt - Derive SAE PT (exact same API as original) + * @groups: Groups array + * @ssid: SSID + * @ssid_len: SSID length + * @password: Password + * @password_len: Password length + * @identifier: Identifier + * + * Returns: SAE PT structure or NULL on failure + */ +struct sae_pt *sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len, const u8 *password, + size_t password_len, const char *identifier) +{ + struct sae_pt *pt = NULL, *last = NULL, *tmp; + int default_groups[] = {19, 0}; + int i; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Deriving PT using PSA APIs only"); + + if (!groups) { + groups = default_groups; + } + for (i = 0; groups[i] > 0; i++) { + tmp = wpa3_psa_derive_pt_group(groups[i], ssid, ssid_len, password, password_len, + identifier); + if (!tmp) { + continue; + } + + if (last) { + last->next = tmp; + } else { + pt = tmp; + } + last = tmp; + } + + return pt; +} + +/** + * sae_deinit_pt - Deinitialize SAE PT (exact same API as original) + * @pt: SAE PT structure + */ +void sae_deinit_pt(struct sae_pt *pt) +{ + if (!pt) { + return; + } + + /* Free PT structure */ + os_free(pt); +} + +/** + * sae_derive_pwe_from_pt_ecc - Derive PWE from PT for ECC (exact same API as original) + * @pt: SAE PT structure + * @addr1: First MAC address + * @addr2: Second MAC address + * + * Returns: ECC point or NULL on failure + */ +struct crypto_ec_point *sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, const u8 *addr1, + const u8 *addr2) +{ + /* For PSA implementation, we use PSA PAKE directly - return NULL */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: PWE derivation from PT not needed with PSA PAKE"); + return NULL; +} + +/** + * sae_derive_pwe_from_pt_ffc - Derive PWE from PT for FFC (exact same API as original) + * @pt: SAE PT structure + * @addr1: First MAC address + * @addr2: Second MAC address + * + * Returns: FFC point or NULL on failure + */ +struct crypto_bignum *sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, const u8 *addr1, + const u8 *addr2) +{ + /* PSA implementation supports ECC groups only (group 19) */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: FFC groups not supported - ECC only"); + return NULL; +} + +/** + * sae_ecc_prime_len_2_hash_len - Convert ECC prime length to hash length (exact same API as + * original) + * @prime_len: Prime length + * + * Returns: Hash length + */ +size_t sae_ecc_prime_len_2_hash_len(size_t prime_len) +{ + /* For PSA implementation, only support group 19 (NIST P-256) */ + return 32; +} + +/** + * sae_ffc_prime_len_2_hash_len - Convert FFC prime length to hash length (exact same API as + * original) + * @prime_len: Prime length + * + * Returns: Hash length + */ +size_t sae_ffc_prime_len_2_hash_len(size_t prime_len) +{ + /* For PSA implementation, we don't use FFC - return default */ + return 32; /* Default to SHA-256 */ +} + +/** + * sae_get_pmk - Get PMK from SAE data (exact same API as original) + * @sae: SAE data + * @pmk: Buffer for PMK + * @pmk_len: Length of PMK buffer + * + * Returns: 0 on success, -1 on failure + */ +int sae_get_pmk(struct sae_data *sae, u8 *pmk, size_t *pmk_len) +{ + struct wpa3_psa_operation *op; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: sae_get_pmk called"); + + if (!sae || !pmk || !pmk_len) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_pmk failed - invalid parameters"); + return -1; + } + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_pmk failed - no PSA operation"); + return -1; + } + + /* Copy the PMK from our operation */ + if (*pmk_len < op->pmk_len) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_pmk failed - buffer too small (%zu < %zu)", + *pmk_len, op->pmk_len); + return -1; + } + + os_memcpy(pmk, op->pmk, op->pmk_len); + *pmk_len = op->pmk_len; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: PMK provided, length: %zu", *pmk_len); + return 0; +} + +/** + * sae_get_pmkid - Get PMKID from SAE data (exact same API as original) + * @sae: SAE data + * @pmkid: Buffer for PMKID + * + * Returns: 0 on success, -1 on failure + */ +int sae_get_pmkid(struct sae_data *sae, u8 *pmkid) +{ + struct wpa3_psa_operation *op; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: sae_get_pmkid called"); + + if (!sae || !pmkid) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_pmkid failed - invalid parameters"); + return -1; + } + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_pmkid failed - no PSA operation"); + return -1; + } + + /* Copy the PMKID from our operation */ + os_memcpy(pmkid, op->pmkid, SAE_PMKID_LEN); + + wpa_printf(MSG_DEBUG, "WPA3-PSA: PMKID provided"); + return 0; +} + +/** + * sae_get_kck - Get KCK from SAE data (exact same API as original) + * @sae: SAE data + * @kck: Buffer for KCK + * @kck_len: Length of KCK buffer + * + * Returns: 0 on success, -1 on failure + */ +int sae_get_kck(struct sae_data *sae, u8 *kck, size_t *kck_len) +{ + struct wpa3_psa_operation *op; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: sae_get_kck called"); + + if (!sae || !kck || !kck_len) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_kck failed - invalid parameters"); + return -1; + } + + op = get_psa_op(sae); + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_kck failed - no PSA operation"); + return -1; + } + + if (*kck_len < SAE_KCK_LEN) { + wpa_printf(MSG_ERROR, "WPA3-PSA: sae_get_kck failed - buffer too small (%zu < %d)", + *kck_len, SAE_KCK_LEN); + return -1; + } + + /* PSA PAKE handles key confirmation internally - KCK is zero */ + /* Return zero KCK for API compatibility */ + os_memcpy(kck, op->kck, SAE_KCK_LEN); + *kck_len = SAE_KCK_LEN; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: KCK provided, length: %zu", *kck_len); + return 0; +} + +/* Internal PSA implementation functions */ + +/** + * wpa3_psa_derive_pt_group - Derive PT for a specific group using PSA only + * @group: SAE group number + * @ssid: SSID + * @ssid_len: SSID length + * @password: Password + * @password_len: Password length + * @identifier: Password identifier + * + * Returns: SAE PT structure or NULL on failure + */ +static struct sae_pt *wpa3_psa_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, + const u8 *password, size_t password_len, + const char *identifier) +{ + struct sae_pt *pt; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Derive PT - group %d (PSA only)", group); + + if (ssid_len > 32) { + return NULL; + } + + /* Handle group selection - group 0 means auto-select, default to group 19 (NIST P-256) */ + if (group == 0) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: PT derivation - Group 0 (auto-select) - using " + "group 19 (NIST P-256)"); + group = 19; + } else if (group != 19) { + wpa_printf(MSG_DEBUG, + "WPA3-PSA: Unsupported group %d for PT derivation (only group 19 NIST " + "P-256 supported)", + group); + return NULL; + } + + pt = os_zalloc(sizeof(*pt)); + if (!pt) { + return NULL; + } + + pt->group = group; + + /* PSA PAKE handles PWE derivation internally, no need for EC/ECC structures */ + pt->ec = NULL; + pt->ecc_pt = NULL; + pt->dh = NULL; + pt->ffc_pt = NULL; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: PT derived successfully for group %d (PSA mode)", group); + return pt; +} + +/** + * wpa3_psa_setup_operation - Setup PSA operation + * @op: PSA operation structure + * @params: Setup parameters + * + * Returns: 0 on success, -1 on failure + */ +static int wpa3_psa_setup_operation(struct wpa3_psa_operation *op, + const struct wpa3_psa_setup_params *params) +{ + psa_status_t status; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t alg; + + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_setup_operation failed - op is NULL"); + return -1; + } + + if (!params) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_setup_operation failed - params is NULL"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Setting up PSA operation"); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Group: %d, H2E: %s, GDH: %s", params->group, + params->h2e ? "yes" : "no", params->gdh ? "yes" : "no"); + + /* Store operation parameters */ + os_memcpy(op->own_addr, params->addr1, ETH_ALEN); + os_memcpy(op->peer_addr, params->addr2, ETH_ALEN); + os_memcpy(op->ssid, params->ssid, params->ssid_len); + op->ssid_len = params->ssid_len; + os_memcpy(op->password, params->password, params->password_len); + op->password_len = params->password_len; + op->group = params->group; + op->h2e_enabled = params->h2e; + op->gdh_enabled = params->gdh; + + /* Set up hash algorithm */ + op->hash_alg = PSA_ALG_SHA_256; + + /* Determine WPA3-SAE algorithm based on parameters */ + if (params->gdh) { + alg = PSA_ALG_WPA3_SAE_GDH(op->hash_alg); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Using GDH algorithm"); + } else { + alg = PSA_ALG_WPA3_SAE_FIXED(op->hash_alg); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Using HnP (fixed) algorithm"); + } + + /* Set up cipher suite */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Setting up cipher suite"); + op->cipher_suite = psa_pake_cipher_suite_init(); + + /* Set algorithm */ + psa_pake_cs_set_algorithm(&op->cipher_suite, alg); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Algorithm set to 0x%08x", alg); + + /* Set primitive */ + psa_pake_primitive_t primitive = + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256); + psa_pake_cs_set_primitive(&op->cipher_suite, primitive); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Primitive set to ECC SECP_R1 256-bit"); + + /* Hash is already included in the algorithm definition */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Hash (SHA-256) included in algorithm"); + + /* Initialize PAKE operation */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Initializing PAKE operation"); + op->pake_op = psa_pake_operation_init(); + + /* Set up password key */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Setting up password key"); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); + + /* Use correct key type based on H2E */ + if (params->h2e) { + psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Using PSA_KEY_TYPE_PASSWORD for H2E"); + } else { + psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + wpa_printf(MSG_DEBUG, "WPA3-PSA: Using PSA_KEY_TYPE_PASSWORD for basic SAE"); + } + + psa_set_key_bits(&attributes, params->password_len * 8); + + /* Set key algorithm for WPA3-SAE */ + psa_set_key_algorithm(&attributes, alg); + + /* Import password as key */ + status = psa_import_key(&attributes, params->password, params->password_len, + &op->password_key); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to import password key: %d", status); + return -1; + } + + /* Set up PAKE operation */ + + status = psa_pake_setup(&op->pake_op, op->password_key, &op->cipher_suite); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to setup PAKE: %d", status); + psa_destroy_key(op->password_key); + return -1; + } + + /* Set user and peer identifiers (MAC addresses) - required for WPA3-SAE */ + wpa_printf(MSG_DEBUG, + "WPA3-PSA: Setting user identifier (own MAC): %02x:%02x:%02x:%02x:%02x:%02x", + op->own_addr[0], op->own_addr[1], op->own_addr[2], op->own_addr[3], + op->own_addr[4], op->own_addr[5]); + status = psa_pake_set_user(&op->pake_op, op->own_addr, ETH_ALEN); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to set user: %d", status); + psa_destroy_key(op->password_key); + return -1; + } + + wpa_printf(MSG_DEBUG, + "WPA3-PSA: Setting peer identifier (peer MAC): %02x:%02x:%02x:%02x:%02x:%02x", + op->peer_addr[0], op->peer_addr[1], op->peer_addr[2], op->peer_addr[3], + op->peer_addr[4], op->peer_addr[5]); + status = psa_pake_set_peer(&op->pake_op, op->peer_addr, ETH_ALEN); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to set peer: %d", status); + psa_destroy_key(op->password_key); + return -1; + } + + /* H2E is handled internally by PSA PAKE - no additional salt setup needed */ + if (params->h2e) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: H2E enabled - handled by PSA PAKE"); + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Operation setup completed successfully"); + return 0; +} + +/** + * wpa3_psa_derive_commit - Derive SAE commit message (M1) using PSA + * @op: PSA operation structure + * + * Returns: 0 on success, -1 on failure + */ +static int wpa3_psa_derive_commit(struct wpa3_psa_operation *op) +{ + psa_status_t status; + u8 commit_data[1024]; /* Buffer for commit message */ + size_t commit_len = 0; + + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_derive_commit failed - op is NULL"); + return -1; + } + + /* Generate our commit message using PSA PAKE */ + status = psa_pake_output(&op->pake_op, PSA_PAKE_STEP_COMMIT, commit_data, + sizeof(commit_data), &commit_len); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_derive_commit failed - psa_pake_output returned %d", + status); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Commit data generated: %zu bytes", commit_len); + + /* Store local commit data for later context calculation */ + if (commit_len > sizeof(op->local_commit)) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_derive_commit failed - commit data too large (%zu > " + "%zu)", + commit_len, sizeof(op->local_commit)); + return -1; + } + + os_memcpy(op->local_commit, commit_data, commit_len); + op->local_commit_len = commit_len; + + /* Also store in shared_key for backward compatibility */ + os_memcpy(op->shared_key, commit_data, commit_len); + op->shared_key_len = commit_len; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Commit message derived successfully (%zu bytes)", + commit_len); + return 0; +} + +/** + * wpa3_psa_process_commit - Process incoming commit message using PSA + * @op: PSA operation structure + * @commit_data: Incoming commit data + * @commit_len: Length of commit data + * + * Returns: 0 on success, -1 on failure + */ +static int wpa3_psa_process_commit(struct wpa3_psa_operation *op, const u8 *commit_data, + size_t commit_len) +{ + psa_status_t status; + uint8_t send_confirm_counter[2] = {0x01, 0x00}; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Processing commit message"); + + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_process_commit failed - op is NULL"); + return -1; + } + + if (!commit_data) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_process_commit failed - commit_data is NULL"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Processing commit data of %zu bytes", commit_len); + + /* DEBUG: Parse and dump commit message contents */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Processing commit data of %zu bytes", commit_len); + if (commit_len != 98) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Commit data wrong size (%zu bytes, expected 98)", + commit_len); + return -1; + } + wpa_hexdump(MSG_DEBUG, "WPA3-PSA: Full commit data", commit_data, commit_len); + + /* Store peer commit data for later context calculation */ + if (commit_len > sizeof(op->peer_commit)) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_process_commit failed - commit data too large (%zu " + "> %zu)", + commit_len, sizeof(op->peer_commit)); + return -1; + } + + os_memcpy(op->peer_commit, commit_data, commit_len); + op->peer_commit_len = commit_len; + + /* Input the peer's commit message to PSA PAKE */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Calling psa_pake_input for commit step"); + status = psa_pake_input(&op->pake_op, PSA_PAKE_STEP_COMMIT, commit_data, commit_len); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_process_commit failed - psa_pake_input returned %d", + status); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Commit message processed successfully"); + + /* Update state to SAE_COMMITTED */ + op->state = SAE_COMMITTED; + wpa_printf(MSG_DEBUG, "WPA3-PSA: Operation state updated to SAE_COMMITTED"); + + /* Set send-confirm counter (required before generating confirms) - as per working test */ + status = psa_pake_input(&op->pake_op, PSA_PAKE_STEP_SEND_CONFIRM, send_confirm_counter, 2); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to set send_confirm counter: %d", status); + return -1; + } + + /* Derive confirm message (M2) */ + status = wpa3_psa_derive_confirm(op); + if (status != 0) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_process_commit failed - " + "wpa3_psa_derive_confirm failed"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Confirm message derived successfully"); + return 0; +} + +/** + * wpa3_psa_derive_confirm - Derive SAE confirm message (M2) using PSA + * @op: PSA operation structure + * + * Returns: 0 on success, -1 on failure + */ +static int wpa3_psa_derive_confirm(struct wpa3_psa_operation *op) +{ + psa_status_t status; + u8 confirm_data[256]; /* Buffer for confirm message */ + size_t confirm_len = 0; + + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_derive_confirm failed - op is NULL"); + return -1; + } + + /* Note: send_confirm is now set in wpa3_psa_process_commit before calling this function */ + + /* Generate confirm message using PSA PAKE */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Calling psa_pake_output for confirm step"); + status = psa_pake_output(&op->pake_op, PSA_PAKE_STEP_CONFIRM, confirm_data, + sizeof(confirm_data), &confirm_len); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_derive_confirm failed - psa_pake_output returned %d", + status); + return -1; + } + + /* Store confirm data for later use */ + if (confirm_len > sizeof(op->shared_key)) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_derive_confirm failed - confirm data too large (%zu " + "> %zu)", + confirm_len, sizeof(op->shared_key)); + return -1; + } + + os_memcpy(op->shared_key, confirm_data, confirm_len); + op->shared_key_len = confirm_len; + + return 0; +} + +/** + * wpa3_psa_process_confirm - Process incoming confirm message using PSA + * @op: PSA operation structure + * @confirm_data: Incoming confirm data + * @confirm_len: Length of confirm data + * + * Returns: 0 on success, -1 on failure + */ +static int wpa3_psa_process_confirm(struct wpa3_psa_operation *op, const u8 *confirm_data, + size_t confirm_len) +{ + psa_status_t status; + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Processing confirm message"); + + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_process_confirm failed - op is NULL"); + return -1; + } + + if (!confirm_data) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_process_confirm failed - confirm_data is NULL"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Processing confirm data of %zu bytes", confirm_len); + + /* Input the peer's confirm message to PSA PAKE */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Calling psa_pake_input for confirm step"); + status = psa_pake_input(&op->pake_op, PSA_PAKE_STEP_CONFIRM, confirm_data, confirm_len); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, + "WPA3-PSA: wpa3_psa_process_confirm failed - psa_pake_input returned %d", + status); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Confirm message processed successfully"); + + /* Derive final keys */ + wpa_printf(MSG_DEBUG, "WPA3-PSA: Deriving final keys"); + status = wpa3_psa_derive_keys(op); + if (status != 0) { + wpa_printf( + MSG_ERROR, + "WPA3-PSA: wpa3_psa_process_confirm failed - wpa3_psa_derive_keys failed"); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPA3-PSA: Final keys derived successfully"); + return 0; +} + +/** + * wpa3_psa_derive_keys - Derive final keys using PSA + * @op: PSA operation structure + * + * Returns: 0 on success, -1 on failure + */ +static int wpa3_psa_derive_keys(struct wpa3_psa_operation *op) +{ + psa_status_t status; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t shared_key_id = PSA_KEY_ID_NULL; + size_t pmk_len; + uint8_t *pmk_buf = NULL; + int ret = -1; + + if (!op) { + wpa_printf(MSG_ERROR, "WPA3-PSA: wpa3_psa_derive_keys failed - op is NULL"); + return -1; + } + + /* PSA PAKE returns PMK directly - no keyseed derivation needed */ + /* Key derivation is handled internally by PSA PAKE */ + + /* Get the shared key from PSA PAKE */ + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_type(&attributes, PSA_KEY_TYPE_DERIVE); + + status = psa_pake_get_shared_key(&op->pake_op, &attributes, &shared_key_id); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to get shared key: %d", status); + return -1; + } + + /* Export the shared key - PSA PAKE returns PMK directly, not the intermediate k */ + pmk_buf = (uint8_t *)os_malloc(32); + if (!pmk_buf) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to allocate memory for PMK"); + goto cleanup; + } + + status = psa_export_key(shared_key_id, pmk_buf, 32, &pmk_len); + if (status != PSA_SUCCESS) { + wpa_printf(MSG_ERROR, "WPA3-PSA: Failed to export shared key (PMK): %d", status); + goto cleanup; + } + + /* PSA PAKE shared key is already the PMK, use it directly */ + os_memcpy(op->pmk, pmk_buf, pmk_len); + op->pmk_len = pmk_len; + + ret = 0; + +cleanup: + if (shared_key_id != PSA_KEY_ID_NULL) { + psa_destroy_key(shared_key_id); + } + if (pmk_buf) { + os_free(pmk_buf); + } + + if (ret == 0) { + wpa_printf(MSG_DEBUG, "WPA3-PSA: Keys derived successfully"); + } + + return ret; +} + +/** + * wpa3_psa_cleanup_operation - Clean up PSA operation resources + */ +static void wpa3_psa_cleanup_operation(struct wpa3_psa_operation *op) +{ + if (!op) { + return; + } + + /* Abort PAKE operation */ + psa_pake_abort(&op->pake_op); + + /* Destroy password key if it exists */ + if (op->password_key != PSA_KEY_ID_NULL) { + psa_destroy_key(op->password_key); + op->password_key = PSA_KEY_ID_NULL; + } + + /* Clear sensitive data */ + os_memset(op->password, 0, sizeof(op->password)); + os_memset(op->shared_key, 0, sizeof(op->shared_key)); + os_memset(op->pmk, 0, sizeof(op->pmk)); + os_memset(op->kck, 0, sizeof(op->kck)); +} From 2a4165769d7f8fb3651c4baf1a8ba32b5d9c9b8a Mon Sep 17 00:00:00 2001 From: Kapil Bhatt Date: Fri, 31 Oct 2025 12:50:29 +0000 Subject: [PATCH 11/14] doc: wifi: Add Wi-Fi direct document Add Wi-Fi direct (p2p mode) document. Signed-off-by: Kapil Bhatt --- doc/nrf/protocols/wifi/index.rst | 1 + doc/nrf/protocols/wifi/wifi_direct.rst | 259 ++++++++++++++++++ .../releases/release-notes-changelog.rst | 3 +- 3 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 doc/nrf/protocols/wifi/wifi_direct.rst diff --git a/doc/nrf/protocols/wifi/index.rst b/doc/nrf/protocols/wifi/index.rst index 6d5b0337e6d..d3d456eb6da 100644 --- a/doc/nrf/protocols/wifi/index.rst +++ b/doc/nrf/protocols/wifi/index.rst @@ -33,6 +33,7 @@ If you want to go through an online training course to familiarize yourself with station_mode/index scan_mode/index sap_mode/index + wifi_direct advanced_modes/index provisioning/index regulatory_support diff --git a/doc/nrf/protocols/wifi/wifi_direct.rst b/doc/nrf/protocols/wifi/wifi_direct.rst new file mode 100644 index 00000000000..ad08a6a55c4 --- /dev/null +++ b/doc/nrf/protocols/wifi/wifi_direct.rst @@ -0,0 +1,259 @@ +.. _ug_wifi_direct: + +Wi-Fi Direct (P2P mode) +####################### + +.. contents:: + :local: + :depth: 2 + +Wi-Fi Direct® (also known as Wi-Fi P2P or peer-to-peer mode) enables direct device-to-device connections without requiring a traditional access point. +The nRF70 Series devices support Wi-Fi Direct, allowing you to establish peer-to-peer connections with other Wi-Fi Direct-capable devices. + +Building with Wi-Fi Direct support +********************************** + +To build an application with Wi-Fi Direct support, use the :ref:`wifi_shell_sample` sample with the ``wifi-p2p`` snippet and external flash for firmware patches. + +Build command +============= + +To build the Wi-Fi shell sample with Wi-Fi Direct (P2P) support, run the following command: + +.. code-block:: console + + west build --pristine --board nrf7002dk/nrf5340/cpuapp -S wifi-p2p -S nrf70-fw-patch-ext-flash + west flash + +Wi-Fi Direct commands +********************* + +The following commands are available for Wi-Fi Direct operations. +Both Wi-Fi shell commands and ``wpa_cli`` commands are provided for each operation. + +Finding peers +============= + +To start discovering Wi-Fi Direct peers, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p find + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_find + +This command initiates the P2P discovery process. +The device scans for other Wi-Fi Direct-capable devices in range. + +Listing discovered peers +======================== + +To view the list of discovered peers, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p peers + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_peers + +This command displays a table of discovered peers with the following information: + +.. code-block:: console + + Num | Device Name | MAC Address | RSSI | Device Type | Config Methods + 1 | Galaxy S22 | D2:39:FA:43:23:C1 | -58 | 10-0050F204-5 | 0x188 + +The columns in the table represent the following attributes: + +* ``Num`` - Sequential number of the peer in the list +* ``Device Name`` - Friendly name of the peer device +* ``MAC Address`` - MAC address of the peer device +* ``RSSI`` - Signal strength in dBm +* ``Device Type`` - WPS device type identifier +* ``Config Methods`` - Supported WPS configuration methods + +Getting peer details +==================== + +To get detailed information about a specific peer, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p peer + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_peer + +For example: + +.. code-block:: console + + wpa_cli p2p_peer D2:39:FA:43:23:C1 + +This command displays detailed information about the specified peer device. + +Connecting to a peer +==================== + +To establish a Wi-Fi Direct connection with a discovered peer: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p connect -g + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_connect go_intent= + +Parameters: + +* ```` - MAC address of the peer device to connect to. +* ```` - WPS provisioning method: + + * ``pin`` - Uses PIN-based WPS authentication. + The command returns a PIN (for example, ``88282282``) that must be entered on the peer device. + * ``pbc`` - Uses Push Button Configuration (PBC) for WPS authentication. + +* ``go_intent`` - Group Owner (GO) intent value ``0-15``: + + * Higher values indicate a stronger preference to become the Group Owner. + * A value of ``15`` forces the device to become the GO. + * A value of ``0`` indicates the device prefers to be a client. + +Example connection using the PIN method: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p connect D2:39:FA:43:23:C1 pin -g 0 + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_connect D2:39:FA:43:23:C1 pin go_intent=0 + +The command outputs a PIN (for example, ``88282282``), which must be entered on the peer device to complete the connection. + +To disconnect from a Wi-Fi Direct connection, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi disconnect + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli disconnect + +Creating a P2P group (GO mode) +============================== + +To create a P2P group and start as the GO, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p group_add + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_group_add + +This command creates a P2P group with the device acting as the GO, allowing other devices to connect to it. + +Inviting a peer to a P2P group +============================== + +To invite a peer to join an existing P2P group, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p invite -g -P + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_invite group= peer= + +Example: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p invite -g wlan0 -P D2:39:FA:43:23:C1 + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_invite group=wlan0 peer=D2:39:FA:43:23:C1 + +Setting P2P power save mode +=========================== + +To enable or disable P2P power save mode, use the following commands: + +.. tabs:: + + .. group-tab:: Wi-Fi shell command + + .. code-block:: console + + wifi p2p power_save + + .. group-tab:: ``wpa_cli`` command + + .. code-block:: console + + wpa_cli p2p_set ps <1|0> diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 8338706384d..e898e8ce318 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -303,7 +303,8 @@ Thread Wi-Fi® ------ -|no_changes_yet_note| +* Added support for Wi-Fi Direct (P2P) mode. + See :ref:`ug_wifi_direct` for details. Applications ============ From a1084226550fdaba48994adaaf328f4501b519d8 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 27 Nov 2025 02:05:55 +0530 Subject: [PATCH 12/14] samples: wifi: wfa_qt_app: Fix twister arg Remove the type, else local twister is ignoring the overlays. Signed-off-by: Chaitanya Tata --- samples/wifi/wfa_qt_app/sample.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/wifi/wfa_qt_app/sample.yaml b/samples/wifi/wfa_qt_app/sample.yaml index e310308e8db..5426d3bc94a 100644 --- a/samples/wifi/wfa_qt_app/sample.yaml +++ b/samples/wifi/wfa_qt_app/sample.yaml @@ -16,7 +16,7 @@ tests: sysbuild: true build_only: true extra_args: - - EXTRA_CONF_FILE:STRING="overlay-netusb.conf;overlay-enterprise.conf" + - EXTRA_CONF_FILE="overlay-netusb.conf;overlay-enterprise.conf" - wfa_qt_app_SNIPPET="wifi-enterprise" integration_platforms: - nrf7002dk/nrf5340/cpuapp From b5added2bb2e11284a52b63df2c53eb820802db4 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 27 Nov 2025 02:08:57 +0530 Subject: [PATCH 13/14] samples: wifi: wfa_qt_app: Fix Flash overflow for enterprise Enable LTO to fix the overflow and also to make room for future increase. Signed-off-by: Chaitanya Tata --- samples/wifi/wfa_qt_app/overlay-enterprise.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/samples/wifi/wfa_qt_app/overlay-enterprise.conf b/samples/wifi/wfa_qt_app/overlay-enterprise.conf index 9010b44fe9b..0ba6a0e953f 100644 --- a/samples/wifi/wfa_qt_app/overlay-enterprise.conf +++ b/samples/wifi/wfa_qt_app/overlay-enterprise.conf @@ -1,3 +1,6 @@ CONFIG_NRF_WIFI_DATA_HEAP_SIZE=80000 CONFIG_WIFI_CREDENTIALS=n CONFIG_NRF70_RX_NUM_BUFS=16 +# Link-time optimizations to fix flash overflow +CONFIG_LTO=y +CONFIG_ISR_TABLES_LOCAL_DECLARATION=y From 6652a645c65892d1607b4973628f82e368db4e17 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 27 Nov 2025 02:25:12 +0530 Subject: [PATCH 14/14] doc: changelog: Add an entry WPA3-SAE PSA Add an entry for WPA3-SAE PSA experimental support. Signed-off-by: Chaitanya Tata --- .../releases/release-notes-changelog.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index e898e8ce318..456d09c243f 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -303,8 +303,10 @@ Thread Wi-Fi® ------ -* Added support for Wi-Fi Direct (P2P) mode. - See :ref:`ug_wifi_direct` for details. +Added: + + * Support for Wi-Fi Direct (P2P) mode, see :ref:`ug_wifi_direct` for details. + * Support for WPA3-SAE using the Oberon PSA PAKE implementation, see :ref:`ug_nrf70_wifi_advanced_security_modes` for details. Applications ============