diff --git a/CODEOWNERS b/CODEOWNERS index 7c12d5103c20..ec7382d94f05 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -122,7 +122,7 @@ /doc/nrf/libraries/bluetooth/mesh.rst @nrfconnect/ncs-paladin-doc /doc/nrf/libraries/bluetooth/services/fast_pair/ @nrfconnect/ncs-si-bluebagel-doc /doc/nrf/libraries/bluetooth/services/fast_pair.rst @nrfconnect/ncs-si-bluebagel-doc -/doc/nrf/libraries/bluetooth/services/wifi_prov.rst @nrfconnect/ncs-wifi-doc +/doc/nrf/libraries/bluetooth/services/wifi_prov_ble.rst @nrfconnect/ncs-wifi-doc /doc/nrf/libraries/caf/ @nrfconnect/ncs-si-muffin-doc @nrfconnect/ncs-si-bluebagel-doc /doc/nrf/libraries/debug/cpu_load.rst @nrfconnect/ncs-doc-leads /doc/nrf/libraries/debug/etb_trace.rst @nrfconnect/ncs-cia-doc @@ -173,6 +173,7 @@ /doc/nrf/libraries/networking/rest_client.rst @nrfconnect/ncs-iot-positioning-doc /doc/nrf/libraries/networking/softap_wifi_provision.rst @nrfconnect/ncs-cia-doc /doc/nrf/libraries/networking/wifi_mgmt_ext.rst @nrfconnect/ncs-cia-doc +/doc/nrf/libraries/networking/wifi_prov_*.rst @nrfconnect/ncs-cia-doc /doc/nrf/libraries/networking/wifi_ready.rst @nrfconnect/ncs-wifi-doc /doc/nrf/libraries/nfc/ @nrfconnect/ncs-si-muffin-doc /doc/nrf/libraries/nrf_rpc/index.rst @nrfconnect/ncs-si-muffin-doc @@ -540,6 +541,7 @@ /samples/tfm/ @nrfconnect/ncs-aegir /samples/wifi/provisioning/ble/ @wentong-li @bama-nordic /samples/wifi/provisioning/softap/ @nrfconnect/ncs-cia +/samples/wifi/provisioning/internal/ @krish2718 @rado17 /samples/wifi/radio_test/ @bama-nordic @sachinthegreen /samples/wifi/scan/ @D-Triveni @bama-nordic /samples/wifi/shell/ @nrfconnect/ncs-co-networking @krish2718 @sachinthegreen @rado17 diff --git a/doc/_utils/redirects.py b/doc/_utils/redirects.py index b2240bef249f..cc84e6095456 100644 --- a/doc/_utils/redirects.py +++ b/doc/_utils/redirects.py @@ -540,7 +540,8 @@ ("libraries/bluetooth_services/services/nus_client", "libraries/bluetooth/services/nus_client"), ("libraries/bluetooth_services/services/rscs", "libraries/bluetooth/services/rscs"), ("libraries/bluetooth_services/services/throughput", "libraries/bluetooth/services/throughput"), - ("libraries/bluetooth_services/services/wifi_prov", "libraries/bluetooth/services/wifi_prov"), + ("libraries/bluetooth_services/services/wifi_prov", "libraries/bluetooth/services/wifi_prov_ble"), + ("libraries/bluetooth/services/wifi_prov", "libraries/bluetooth/services/wifi_prov_ble"), ("libraries/networking/nrf_cloud_agps", "libraries/networking/nrf_cloud_agnss"), # nRF Cloud A-GNSS ("libraries/bootloader/index", "libraries/security/bootloader/index"), # Bootloader libraries (landing) ("libraries/bootloader/bl_crypto", "libraries/security/bootloader/bl_crypto"), # Bootloader crypto diff --git a/doc/nrf/libraries/bluetooth/services/wifi_prov_ble.rst b/doc/nrf/libraries/bluetooth/services/wifi_prov_ble.rst new file mode 100644 index 000000000000..323531b2fd1c --- /dev/null +++ b/doc/nrf/libraries/bluetooth/services/wifi_prov_ble.rst @@ -0,0 +1,119 @@ +.. _wifi_prov_readme: +.. _lib_wifi_prov_ble: + +Wi-Fi provisioning Bluetooth LE transport +######################################### + +.. contents:: + :local: + :depth: 2 + +This library implements the Bluetooth® GATT transport layer for the Wi-Fi® provisioning service. +It provides the Bluetooth LE-specific implementation of the transport interface defined by the core Wi-Fi provisioning library. +The Bluetooth LE transport layer is designed to work with the core Wi-Fi provisioning library located at :file:`subsys/net/lib/wifi_prov/` directory. + +Overview +******** + +The Bluetooth LE transport layer is responsible for: + +* GATT service interface - Defines the GATT service and serves as the transport layer for the provisioning protocol. +* Bluetooth LE-specific message handling - Implements the transport functions required by the core library. +* Connection management - Manages Bluetooth LE connections and handles characteristic notifications and indications. + +Configuration +************* + +To use this library, enable the :kconfig:option:`CONFIG_BT_WIFI_PROV` Kconfig option. + +Service declaration +******************* + +The Wi-Fi Provisioning Service is instantiated as a primary service. +Set the service UUID value as defined in the following table: + +========================== ======================================== +Service name UUID +Wi-Fi Provisioning Service ``14387800-130c-49e7-b877-2881c89cb258`` +========================== ======================================== + +Service characteristics +======================= + +The UUID value of characteristics are defined in the following table. + +========================== ======================================== +Characteristic name UUID +Information ``14387801-130c-49e7-b877-2881c89cb258`` +Operation Control Point ``14387802-130c-49e7-b877-2881c89cb258`` +Data Out ``14387803-130c-49e7-b877-2881c89cb258`` +========================== ======================================== + +The characteristic requirements of the Wi-Fi Provisioning Service are shown in the following table. + ++-----------------+-------------+-------------+-------------+-------------+ +| Characteristic | Requirement | Mandatory | Optional | Security | +| name | | properties | properties | permissions | ++=================+=============+=============+=============+=============+ +| Information | Mandatory | Read | | No security | +| | | | | required | ++-----------------+-------------+-------------+-------------+-------------+ +| Operation | Mandatory | Indicate, | | Encryption | +| Control | | Write | | required | +| Point | | | | | ++-----------------+-------------+-------------+-------------+-------------+ +| Operation | Mandatory | Read, Write | | Encryption | +| Control | | | | required | +| Point | | | | | +| - Client | | | | | +| Characteristic | | | | | +| Configuration | | | | | +| descriptor | | | | | ++-----------------+-------------+-------------+-------------+-------------+ +| Data Out | Mandatory | Notify | | Encryption | +| | | | | required | ++-----------------+-------------+-------------+-------------+-------------+ +| Data Out | Mandatory | Read, Write | | Encryption | +| - Client | | | | required | +| Characteristic | | | | | +| Configuration | | | | | +| descriptor | | | | | ++-----------------+-------------+-------------+-------------+-------------+ + +The purpose of each characteristic is as follows: + +* ``Information`` - For client to get ``Info`` message from server. +* ``Operation Control Point`` - For client to send ``Request`` message to server, and server to send ``Response`` message to client. +* ``Data Out`` - For server to send ``Result`` message to the client. + +Transport interface implementation +********************************** + +The Bluetooth LE transport layer implements the transport interface defined by the core Wi-Fi provisioning library: + +* :c:func:`wifi_prov_send_rsp` - Sends Response messages through Bluetooth LE indications on the Operation Control Point characteristic. +* :c:func:`wifi_prov_send_result` - Sends Result messages through Bluetooth LE notifications on the Data Out characteristic. + +The transport layer also handles: + +* Receiving request messages from the Operation Control Point characteristic. +* Providing info messages through the Information characteristic. +* Managing Bluetooth LE connection state and characteristic subscriptions. + +Dependencies +************ + +The Bluetooth LE transport layer depends on: + +* :ref:`lib_wifi_prov_core` +* Bluetooth stack (:kconfig:option:`CONFIG_BT`) +* nanopb for protobuf message handling + + +API documentation +***************** + +| Header file: :file:`include/net/wifi_prov_core/wifi_prov_core.h` +| Source files: :file:`subsys/bluetooth/services/wifi_prov` + +.. doxygengroup:: bt_wifi_prov diff --git a/doc/nrf/libraries/bluetooth/services/wifi_prov.rst b/doc/nrf/libraries/networking/wifi_prov_core.rst similarity index 65% rename from doc/nrf/libraries/bluetooth/services/wifi_prov.rst rename to doc/nrf/libraries/networking/wifi_prov_core.rst index 607ac867f759..f9493f0a4f13 100644 --- a/doc/nrf/libraries/bluetooth/services/wifi_prov.rst +++ b/doc/nrf/libraries/networking/wifi_prov_core.rst @@ -1,26 +1,25 @@ -.. _wifi_prov_readme: +.. _lib_wifi_prov_core: -Wi-Fi Provisioning Service -########################## +Wi-Fi Provisioning Core +####################### .. contents:: :local: :depth: 2 -This library implements a Bluetooth® GATT service for Wi-Fi® provisioning. -It is an implementation of the server side of the Wi-Fi provisioning protocol defined by Nordic Semiconductor. -It is to be used with the :ref:`wifi_provisioning` sample. -The Wi-Fi Provisioning Service forms a complete reference solution, together with the mobile application. -For details, see the :ref:`wifi_provisioning` sample documentation. +This library implements the core Wi-Fi® provisioning functionality that is transport-agnostic. +It provides protocol implementation, message handling, and configuration management for Wi-Fi provisioning. +The core library is designed to work with various transport layers (For example, Bluetooth® LE, Wi-Fi SoftAP) through a defined transport interface. Overview ******** -The service is divided into three parts: +The core library is responsible for: -* GATT service interface: Defines the GATT service and serves as the transport layer for the provisioning protocol. -* Task and event handling component: Implements the provisioning protocol. -* Configuration management component: Manages the provisioning data (in RAM and flash) accessed by multiple threads. +* Protocol implementation - Implements the Wi-Fi provisioning protocol using Protocol Buffers. +* Message handling - Processes Request messages and generates Response/Result messages. +* Configuration management - Manages Wi-Fi configurations in RAM and flash. +* Transport interface - Provides weak transport functions that can be overridden by transport layers. .. _wifi_provisioning_protocol: @@ -109,7 +108,7 @@ The protocol defines four message types: =================== ======================= ======================== ======================================================================================= These definitions are available as :file:`.proto` files in the library path. -See all definitions in the :file:`subsys/bluetooth/services/wifi_prov/proto/` folder. +See all definitions in the :file:`subsys/net/lib/wifi_prov/proto/` folder. Workflow ======== @@ -163,76 +162,15 @@ In the ``Response`` message, the ``request_op_code`` is ``FORGET_CONFIG``, and t When the connection state changes or an attempt fails, the target will set up a ``Result`` message, and the ``state`` field indicates the current state of the Wi-Fi, and the ``reason`` field indicates the failure reason. -Service declaration +Transport interface ******************* -The Wi-Fi Provisioning Service is instantiated as a primary service. -Set the service UUID value as defined in the following table. - -========================== ======================================== -Service name UUID -Wi-Fi Provisioning Service ``14387800-130c-49e7-b877-2881c89cb258`` -========================== ======================================== - -Service characteristics -======================= - -The UUID value of characteristics are defined in the following table. - -========================== ======================================== -Characteristic name UUID -Information ``14387801-130c-49e7-b877-2881c89cb258`` -Operation Control Point ``14387802-130c-49e7-b877-2881c89cb258`` -Data Out ``14387803-130c-49e7-b877-2881c89cb258`` -========================== ======================================== - -The characteristic requirements of the Wi-Fi Provisioning Service are shown in the following table. - -+-----------------+-------------+-------------+-------------+-------------+ -| Characteristic | Requirement | Mandatory | Optional | Security | -| name | | properties | properties | permissions | -+=================+=============+=============+=============+=============+ -| Information | Mandatory | Read | | No security | -| | | | | required | -+-----------------+-------------+-------------+-------------+-------------+ -| Operation | Mandatory | Indicate, | | Encryption | -| Control | | Write | | required | -| Point | | | | | -+-----------------+-------------+-------------+-------------+-------------+ -| Operation | Mandatory | Read, Write | | Encryption | -| Control | | | | required | -| Point | | | | | -| - Client | | | | | -| Characteristic | | | | | -| Configuration | | | | | -| descriptor | | | | | -+-----------------+-------------+-------------+-------------+-------------+ -| Data Out | Mandatory | Notify | | Encryption | -| | | | | required | -+-----------------+-------------+-------------+-------------+-------------+ -| Data Out | Mandatory | Read, Write | | Encryption | -| - Client | | | | required | -| Characteristic | | | | | -| Configuration | | | | | -| descriptor | | | | | -+-----------------+-------------+-------------+-------------+-------------+ - -The purpose of each characteristic is as follows: - -* ``Information``: For client to get ``Info`` message from server. -* ``Operation Control Point``: For client to send ``Request`` message to server, and server to send ``Response`` message to client. -* ``Data Out``: For server to send ``Result`` message to the client. - -It takes the functions exposed by the task and event handling part of reading the ``Info`` message and receiving the ``Request`` message as the callbacks of corresponding characteristics. -It provides functions for the task and event handling part to send ``Response`` and ``Result`` messages. - -Task and event handling -*********************** - -The service uses `nanopb`_ to instantiate the protocol buffers-based, platform-independent messages in the C language. - -It exposes the functions of reading the ``Info`` message and receiving the ``Request`` message to transport layer. -It uses the function of sending ``Response`` and ``Result`` messages provided by the transport layer to send these messages. +The core library provides a transport-agnostic interface through weak functions that can be overridden by transport layers: + +* :c:func:`wifi_prov_send_rsp` - Sends Response messages to the transport layer +* :c:func:`wifi_prov_send_result` - Sends Result messages to the transport layer + +Transport layers must implement these functions to handle the actual message transmission (for example, Bluetooth LE indications and notifications, USB transfers, UART transmissions). Configuration management ************************ @@ -243,10 +181,19 @@ The component has one slot in RAM to save the configurations. You can save the configuration in flash or RAM during provisioning. +Dependencies +************ + +The core library depends on: + +* nanopb for protobuf message handling. +* Wi-Fi credentials library for configuration management (:ref:`lib_wifi_credentials`). +* Wi-Fi management interface for network operations (:ref:`wifi_mgmt`). + API documentation ***************** -| Header file: :file:`include/bluetooth/services/wifi_provisioning.h` -| Source files: :file:`subsys/bluetooth/services/wifi_prov` +| Header file: :file:`include/net/wifi_prov_core/wifi_prov_core.h` +| Source files: :file:`subsys/net/lib/wifi_prov_core` -.. doxygengroup:: bt_wifi_prov +.. doxygengroup:: wifi_prov_core diff --git a/doc/nrf/libraries/networking/wifi_prov_tools.rst b/doc/nrf/libraries/networking/wifi_prov_tools.rst new file mode 100644 index 000000000000..902ddbfd6a84 --- /dev/null +++ b/doc/nrf/libraries/networking/wifi_prov_tools.rst @@ -0,0 +1,179 @@ +.. _wifi_prov_tools: + +Wi-Fi Provisioning Configuration Generator +########################################## + +.. contents:: + :local: + :depth: 2 + +This tool generates Protocol Buffers (protobuf) configuration messages for Wi-Fi® provisioning, supporting both EAP-TLS (Enterprise) and Personal (WPA2-PSK/WPA3-PSK) modes. + +Prerequisites +************* + +The script automatically generates its own protobuf dependencies when needed. +No manual setup is required. + +.. note:: + The script requires the ``protoc`` compiler to be available in your PATH. + This is typically included in the NCS toolchain. + +Usage +***** + +The script supports two main modes: + +Enterprise mode +=============== + +Run the following command for enterprise networks using certificate-based authentication, EAP-TLS is used as an example: + +.. code-block:: bash + + python3 generate_wifi_prov_config.py "MyWi-Fi" "AA:BB:CC:DD:EE:FF" \ + -d /path/to/certs \ + -j -o eap_tls_config.json + +The following are the required certificate files in the cert directory: + +* :file:`ca.pem` - CA certificate +* :file:`client.pem` - Client certificate +* :file:`client-key.pem` - Client private key +* :file:`ca2.pem` - Secondary CA certificate +* :file:`client2.pem` - Secondary client certificate +* :file:`client-key2.pem` - Secondary client private key + +Personal mode +============= + +Run the following commands for home or office networks using passphrase authentication: + +.. code-block:: bash + + # WPA2-PSK + python3 generate_wifi_prov_config.py "MyWi-Fi" "AA:BB:CC:DD:EE:FF" \ + -w "mypassword" -a 3 -j -o personal_config.json + + # WPA3-SAE + python3 generate_wifi_prov_config.py "MyWi-Fi" "AA:BB:CC:DD:EE:FF" \ + -w "mypassword" -a 10 -j -o wpa3_config.json + +Command line options +==================== + +.. list-table:: Command-line arguments + :header-rows: 1 + :widths: 20 40 40 + + * - Argument + - Description + - Default values and notes + + * - ``ssid`` + - Wi-Fi network name + - (positional) + + * - ``bssid`` + - Wi-Fi BSSID (MAC address) + - (positional) + + * - ``-d, --cert-dir`` + - Certificate directory (for EAP-TLS mode) + - (optional) + + * - ``-w, --passphrase`` + - Wi-Fi passphrase (for Personal mode) + - (optional) + + * - ``-a, --auth-mode`` + - Authentication mode + - 0=OPEN, 1=WEP, 2=WPA_PSK, 3=WPA2_PSK, 4=WPA_WPA2_PSK, 5=WPA2_ENTERPRISE, 6=WPA3_PSK, 7=NONE, 8=PSK, 9=PSK_SHA256, 10=SAE, 11=WAPI, 12=EAP, 13=WPA_AUTO_PERSONAL, 14=DPP, 15=EAP_PEAP_MSCHAPV2, 16=EAP_PEAP_GTC, 17=EAP_TTLS_MSCHAPV2, 18=EAP_PEAP_TLS, 19=FT_PSK, 20=FT_SAE, 21=FT_EAP, 22=FT_EAP_SHA384, 23=SAE_EXT_KEY; default: 5=WPA2_ENTERPRISE + + * - ``-c, --channel`` + - Wi-Fi channel + - default: 0 + + * - ``-b, --band`` + - Wi-Fi band + - 0=AUTO, 1=2.4GHz, 2=5GHz; default: 0 + + * - ``-i, --identity`` + - EAP identity + - default: user@example.com + + * - ``-p, --password`` + - EAP password + - default: user_password + + * - ``-k, --private-key-passwd`` + - Primary private key password + - (optional) + + * - ``-k2, --private-key-passwd2`` + - Secondary private key password + - (optional) + + * - ``-o, --output`` + - Output file + - (optional) + + * - ``-j, --json`` + - Display JSON configuration + - (optional) + +Output +****** + +The tool always displays the encoded protobuf format: + +Encoded Protobuf (Base64) - Base64-encoded protobuf string for transmission + +When using the ``-j`` flag, JSON configuration is also displayed for reference. + +When using the ``-o`` option, files are saved in the specified format: + +* ``-j -o file.json`` - Save as JSON file +* ``-o file.bin`` - Save as binary protobuf file + +Configuration details +********************* + +The generated configuration includes: + +* *SSID* - Wi-Fi network name +* *BSSID* - Access point MAC address +* *Channel* - Wi-Fi channel number +* *Band* - 2.4GHz or 5GHz +* *Authentication Mode* - WPA-PSK, WPA2-PSK, WPA2-ENTERPRISE, WPA3-PSK +* *Mode* - EAP-TLS (Enterprise) or Personal +* *Identity* - EAP identity (for Enterprise mode) +* *Passphrase* - Wi-Fi password (for Personal mode, masked in output) + +Error handling +************** + +The tool performs the following validation checks: + +* Certificate directory existence (for EAP-TLS mode) +* Required certificate files presence +* Valid authentication mode selection +* Proper parameter combinations + +The following are common error messages you may encounter while using the tool: + +* "Certificate directory does not exist" +* "Missing required certificate files" +* "Must specify either --cert-dir (EAP-TLS) or --passphrase (Personal)" +* "Auth mode X is not valid for EAP-TLS" (for invalid EAP auth modes) + +Integration +*********** + +The generated protobuf messages can be used with the following systems: + +* Nordic Semiconductor's Wi-Fi provisioning service +* Zephyr RTOS Wi-Fi stack +* Custom Wi-Fi provisioning applications + +The binary protobuf format is suitable for transmission over Bluetooth LE or other communication channels. diff --git a/include/bluetooth/services/wifi_provisioning.h b/include/bluetooth/services/wifi_provisioning.h index 1ee6ca104708..225ca764ce0b 100644 --- a/include/bluetooth/services/wifi_provisioning.h +++ b/include/bluetooth/services/wifi_provisioning.h @@ -44,26 +44,6 @@ extern "C" { #define BT_UUID_PROV_DATA_OUT \ BT_UUID_DECLARE_128(BT_UUID_PROV_DATA_OUT_VAL) -/** - * @def PROV_SVC_VER - * - * Firmware version. - */ -#define PROV_SVC_VER 0x01 - -/** - * @brief Get provisioning state. - * - * @return true if device is provisioned, false otherwise. - */ -bool bt_wifi_prov_state_get(void); - -/** - * @brief Initialize the provisioning module. - * - * @return 0 if module initialized successfully, negative error code otherwise. - */ -int bt_wifi_prov_init(void); #ifdef __cplusplus } diff --git a/include/net/wifi_prov_core/wifi_prov_core.h b/include/net/wifi_prov_core/wifi_prov_core.h new file mode 100644 index 000000000000..678b06cac333 --- /dev/null +++ b/include/net/wifi_prov_core/wifi_prov_core.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef ZEPHYR_INCLUDE_NET_WIFI_PROV_H_ +#define ZEPHYR_INCLUDE_NET_WIFI_PROV_H_ + +#include +#include + +#include "request.pb.h" +#include "response.pb.h" +#include "result.pb.h" +#include "version.pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief WiFi Provisioning API + * @defgroup wifi_prov_core WiFi Provisioning + * @{ + */ + +/* Message size constants */ +#define INFO_MSG_MAX_LENGTH Info_size +#define REQUEST_MSG_MAX_LENGTH Request_size +#define RESPONSE_MSG_MAX_LENGTH Response_size +#define RESULT_MSG_MAX_LENGTH Result_size + +/* WiFi Provisioning Service Version */ +#define PROV_SVC_VER 0x01 + +/** + * @brief Handle received WiFi provisioning request + * + * @param req_stream Request buffer containing encoded protobuf message + * @return 0 on success, negative error code on failure + */ +int wifi_prov_recv_req(struct net_buf_simple *req_stream); + +/** + * @brief Get WiFi provisioning service information + * + * @param info Buffer to store encoded info message + * @return 0 on success, negative error code on failure + */ +int wifi_prov_get_info(struct net_buf_simple *info); + +/** + * @brief Initialize WiFi provisioning service + * + * @return 0 on success, negative error code on failure + */ +int wifi_prov_init(void); + +/** + * @brief Get WiFi provisioning state + * + * @return true if WiFi provisioning is active, false otherwise + */ +bool wifi_prov_state_get(void); + +/** + * @brief Send response to the transport layer + * + * @param rsp Response buffer to send + * @return 0 on success, negative error code on failure + */ +int wifi_prov_send_rsp(struct net_buf_simple *rsp); + +/** + * @brief Send result to the transport layer + * + * @param result Result buffer to send + * @return 0 on success, negative error code on failure + */ +int wifi_prov_send_result(struct net_buf_simple *result); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_NET_WIFI_PROV_H_ */ diff --git a/samples/wifi/provisioning/ble/CMakeLists.txt b/samples/wifi/provisioning/ble/CMakeLists.txt index 613f038712de..bd3de92f3374 100644 --- a/samples/wifi/provisioning/ble/CMakeLists.txt +++ b/samples/wifi/provisioning/ble/CMakeLists.txt @@ -10,3 +10,7 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(wifi_ble_provisioning) target_sources(app PRIVATE src/main.c) + +zephyr_library_include_directories($) + +target_link_libraries(app PRIVATE wifi_prov_ble) diff --git a/samples/wifi/provisioning/ble/README.rst b/samples/wifi/provisioning/ble/README.rst index b464f928f61e..7bde760625ef 100644 --- a/samples/wifi/provisioning/ble/README.rst +++ b/samples/wifi/provisioning/ble/README.rst @@ -33,7 +33,7 @@ The sample requires a smartphone (configurator) with Nordic Semiconductor's nRF Overview ******** -With this sample, you can provision a Wi-Fi device that lacks input or output capability, using the :ref:`wifi_prov_readme` library. +With this sample, you can provision a Wi-Fi device that lacks input or output capability, using the :ref:`lib_wifi_prov_ble` library. The sample is divided into three parts: * Task and event handling component: Handles provisioning-related tasks and events. @@ -149,7 +149,7 @@ Dependencies This sample uses the following |NCS| libraries: -* :ref:`wifi_prov_readme` +* :ref:`lib_wifi_prov_ble` * :ref:`Wi-Fi credentials ` This sample also uses a module that can be found in the following location in the |NCS| folder structure: diff --git a/samples/wifi/provisioning/ble/prj.conf b/samples/wifi/provisioning/ble/prj.conf index ed114fc4543f..fa3b94a54661 100644 --- a/samples/wifi/provisioning/ble/prj.conf +++ b/samples/wifi/provisioning/ble/prj.conf @@ -82,7 +82,7 @@ CONFIG_BT_BUF_ACL_TX_SIZE=151 # Increased BT RX stack size because BLE and Wi-Fi operations # run in the same BT RX workqueue thread during provisioning. -CONFIG_BT_RX_STACK_SIZE=5400 +CONFIG_BT_RX_STACK_SIZE=22000 CONFIG_BT_BONDABLE=n CONFIG_BT_DEVICE_NAME_DYNAMIC=y @@ -101,6 +101,7 @@ CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES=1 CONFIG_NANOPB=y CONFIG_BT_WIFI_PROV=y +CONFIG_WIFI_PROV_CORE=y CONFIG_BT_WIFI_PROV_LOG_LEVEL_INF=y # Setting BT supervision timeout to 75units (750ms) to avoid timeout of BT connection when radio is granted to Wi-Fi during scan. diff --git a/samples/wifi/provisioning/ble/src/main.c b/samples/wifi/provisioning/ble/src/main.c index 8c400de4ccda..1332dea0197b 100644 --- a/samples/wifi/provisioning/ble/src/main.c +++ b/samples/wifi/provisioning/ble/src/main.c @@ -19,6 +19,7 @@ #include #include +#include #include #ifdef CONFIG_WIFI_PROV_ADV_DATA_UPDATE @@ -74,7 +75,7 @@ static void update_wifi_status_in_adv(void) prov_svc_data[ADV_DATA_VERSION_IDX] = PROV_SVC_VER; /* If no config, mark it as unprovisioned. */ - if (!bt_wifi_prov_state_get()) { + if (!wifi_prov_state_get()) { prov_svc_data[ADV_DATA_FLAG_IDX] &= ~ADV_DATA_FLAG_PROV_STATUS_BIT; } else { prov_svc_data[ADV_DATA_FLAG_IDX] |= ADV_DATA_FLAG_PROV_STATUS_BIT; @@ -261,7 +262,7 @@ int main(void) printk("Bluetooth initialized.\n"); - rc = bt_wifi_prov_init(); + rc = wifi_prov_init(); if (rc == 0) { printk("Wi-Fi provisioning service starts successfully.\n"); } else { diff --git a/samples/wifi/provisioning/internal/CMakeLists.txt b/samples/wifi/provisioning/internal/CMakeLists.txt new file mode 100644 index 000000000000..912d0582f05b --- /dev/null +++ b/samples/wifi/provisioning/internal/CMakeLists.txt @@ -0,0 +1,61 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(wifi_prov_internal) + +# WiFi Provisioning Configuration +if(CONFIG_WIFI_PROV_CONFIG) + # Generate sample WiFi configuration + set(PROTO_DIR ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/wifi_prov_core/proto) + set(WIFI_CONFIG_SCRIPT ${PROTO_DIR}/generate_wifi_prov_config.py) + set(WIFI_CONFIG_BIN ${CMAKE_CURRENT_BINARY_DIR}/wifi_config.bin) + set(WIFI_CONFIG_INC ${CMAKE_CURRENT_BINARY_DIR}/wifi_config.inc) + + # Note: nanopb generation is already handled by wifi_prov_core library + # which is linked to the app target below + + # Build the command with conditional certificate directory + set(WIFI_CONFIG_CMD ${Python3_EXECUTABLE} ${WIFI_CONFIG_SCRIPT} + "${CONFIG_WIFI_PROV_SSID}" "${CONFIG_WIFI_PROV_BSSID}" + -w "${CONFIG_WIFI_PROV_PASSPHRASE}" + -a ${CONFIG_WIFI_PROV_AUTH_MODE} -c ${CONFIG_WIFI_PROV_CHANNEL} -b ${CONFIG_WIFI_PROV_BAND} + -o ${WIFI_CONFIG_BIN}) + + # Add certificate directory if specified + if(CONFIG_WIFI_PROV_CERT_DIR) + list(APPEND WIFI_CONFIG_CMD -d "${CONFIG_WIFI_PROV_CERT_DIR}") + endif() + + # Generate the binary file + add_custom_command( + OUTPUT ${WIFI_CONFIG_BIN} + COMMAND ${WIFI_CONFIG_CMD} + DEPENDS ${WIFI_CONFIG_SCRIPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating sample WiFi configuration" + ) + + # Use Zephyr's generate_inc_file_for_target to create the .inc file + generate_inc_file_for_target(app ${WIFI_CONFIG_BIN} ${WIFI_CONFIG_INC}) + + # Include generated header directory + target_include_directories(app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + # Include WiFi provisioning library headers and link against it + target_include_directories(app PRIVATE ${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/wifi_prov_core) + target_include_directories(app PRIVATE $) + target_include_directories(app PRIVATE ${ZEPHYR_BASE}/modules/lib/nanopb) + target_link_libraries(app PRIVATE wifi_prov_core) +endif() # CONFIG_WIFI_PROV_CONFIG + +target_sources(app PRIVATE + src/main.c + src/prov.c + src/wifi_prov_transport_stub.c +) diff --git a/samples/wifi/provisioning/internal/Kconfig b/samples/wifi/provisioning/internal/Kconfig new file mode 100644 index 000000000000..e12da1a18ba1 --- /dev/null +++ b/samples/wifi/provisioning/internal/Kconfig @@ -0,0 +1,145 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menuconfig WIFI_PROV_CONFIG + bool "WiFi provisioning configuration" + default y + help + This option enables WiFi provisioning configuration. + +config WIFI_PROV_SSID + string "WiFi SSID for provisioning" + default "SampleWiFi" + help + SSID for the WiFi network to be provisioned. + +config WIFI_PROV_BSSID + string "WiFi BSSID for provisioning" + default "00:11:22:33:44:55" + help + BSSID (MAC address) for the WiFi network to be provisioned. + +config WIFI_PROV_PASSPHRASE + string "WiFi passphrase for provisioning" + default "samplepassword" + help + Passphrase for the WiFi network to be provisioned. + +config WIFI_PROV_AUTH_MODE + int "WiFi authentication mode" + default 4 + range 0 23 + help + Authentication mode for the WiFi network. + 0: Open, 1: WEP, 2: WPA_PSK, 3: WPA2_PSK, etc. + +config WIFI_PROV_CHANNEL + int "WiFi channel" + default 0 + range 0 233 + help + Channel for the WiFi network (2.4GHz band). + +config WIFI_PROV_BAND + int "WiFi band" + default 0 + range 0 2 + help + Band for the WiFi network. + 1: 2.4GHz, 2: 5GHz + +config WIFI_PROV_CERT_DIR + string "Certificate directory path" + default "" + help + Path to directory containing certificates for enterprise WiFi networks. + Leave empty for personal WiFi networks. + Certificates should be in PEM format. + +config WIFI_PROV_PRIVATE_KEY_PASSWD + string "Primary private key password" + default "" + help + Password for the primary private key file. + Leave empty to use EAP password. + +config WIFI_PROV_PRIVATE_KEY_PASSWD2 + string "Secondary private key password" + default "" + help + Password for the secondary private key file. + Leave empty to use EAP password. + +config WIFI_PROV_IDENTITY + string "EAP identity" + default "user@example.com" + help + EAP identity for enterprise WiFi networks. + +config WIFI_PROV_PASSWORD + string "EAP password" + default "user_password" + help + EAP password for enterprise WiFi networks. + +config WIFI_PROV_VOLATILE_MEMORY + bool "Use volatile memory" + default n + help + Store WiFi configuration in volatile memory. + If disabled, configuration is stored persistently. + +config WIFI_PROV_SCAN_BAND + int "WiFi scan band" + default 0 + range 0 2 + help + Band for WiFi scanning. + 0: Any band, 1: 2.4GHz, 2: 5GHz + +config WIFI_PROV_SCAN_PASSIVE + bool "Passive scan mode" + default n + help + Enable passive scanning mode. + If disabled, active scanning is used. + +config WIFI_PROV_SCAN_PERIOD_MS + int "Scan period in milliseconds" + default 0 + range 0 60000 + help + Period between WiFi scans in milliseconds. + 0 means no periodic scanning. + +config WIFI_PROV_SCAN_GROUP_CHANNELS + int "Scan group channels" + default 0 + range 0 255 + help + Group channels for WiFi scanning. + 0 means no grouping. + +config WIFI_PROV_MAX_DATA_SIZE + int "Maximum Protobuf Data Size" + default 16384 + range 1024 16384 + help + Maximum size for protobuf data in bytes. + +config WIFI_PROV_MAX_BASE64_SIZE + int "Maximum Base64 Input Size" + default 32768 + range 2048 32768 + help + Maximum size for base64 input string in bytes. + +module = WIFI_PROV_INTERNAL +module-dep = LOG +module-str = Log level for sample +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +source "Kconfig.zephyr" diff --git a/samples/wifi/provisioning/internal/README.rst b/samples/wifi/provisioning/internal/README.rst new file mode 100644 index 000000000000..87f5c952a00e --- /dev/null +++ b/samples/wifi/provisioning/internal/README.rst @@ -0,0 +1,364 @@ +.. _wifi_provisioning_internal_sample: + +Wi-Fi Provisioning Internal Sample +################################## + +.. contents:: + :local: + :depth: 2 + +This sample demonstrates the internal Wi-Fi® provisioning functionality using the decoupled Wi-Fi provisioning library. +The sample demonstrates proper integration of the Wi-Fi provisioning library with comprehensive testing capabilities and human-readable protocol decoding. +It provides shell commands to test all supported Wi-Fi provisioning operations. + +Requirements +************ + +The sample supports the following development kits: + +.. table-from-sample-yaml:: + +Overview +******** + +The sample uses the core Wi-Fi provisioning library (``wifi_prov_core``) with a transport stub implementation that decodes and logs protobuf messages. +This allows testing of the provisioning protocol without requiring a Bluetooth® transport layer. + +Features +******** + +This sample demonstrates the following features: + +* Transport Decoupling - Uses the core Wi-Fi provisioning library without Bluetooth® dependencies. +* Protobuf Decoding - Automatically decodes and logs all requests and responses. +* Comprehensive Testing - Shell commands for all supported operations. +* Raw Data Support - Ability to send custom binary data for testing. +* Configurable Generation - Wi-Fi configuration generated from Kconfig options. + +Architecture +************ + +The sample demonstrates the decoupled architecture: + +* Core Library - ``wifi_prov_core`` handles the protobuf protocol and business logic. +* Transport Stub - ``wifi_prov_transport_stub.c`` provides mock transport functions. +* Shell Interface - ``prov.c`` implements shell commands for testing. +* Configuration - Generated from Kconfig options at build time. + +Configuration +************* + +|config| + +Configuration options +===================== + +The following sample-specific Kconfig options are used in this sample (located in :file:`samples/wifi/provisioning/internal/Kconfig`): + +.. _CONFIG_WIFI_PROV_CONFIG: + +CONFIG_WIFI_PROV_CONFIG + This option enables Wi-Fi provisioning. + +.. _CONFIG_WIFI_PROV_SSID: + +CONFIG_WIFI_PROV_SSID + This option specifies the Wi-Fi SSID. + +.. _CONFIG_WIFI_PROV_BSSID: + +CONFIG_WIFI_PROV_BSSID + This option specifies the Wi-Fi BSSID. + +.. _CONFIG_WIFI_PROV_PASSPHRASE: + +CONFIG_WIFI_PROV_PASSPHRASE + This option specifies the Wi-Fi passphrase. + +.. _CONFIG_WIFI_PROV_AUTH_MODE: + +CONFIG_WIFI_PROV_AUTH_MODE + This option specifies the Wi-Fi authentication mode. + +.. _CONFIG_WIFI_PROV_CHANNEL: + +CONFIG_WIFI_PROV_CHANNEL + This option specifies the Wi-Fi channel. + +.. _CONFIG_WIFI_PROV_BAND: + +CONFIG_WIFI_PROV_BAND + This option specifies the Wi-Fi band. + +.. _CONFIG_WIFI_PROV_CERT_DIR: + +CONFIG_WIFI_PROV_CERT_DIR + This option specifies the Wi-Fi certificate directory. + +.. _CONFIG_WIFI_PROV_PRIVATE_KEY_PASSWD: + +CONFIG_WIFI_PROV_PRIVATE_KEY_PASSWD + This option specifies the Wi-Fi private key password. + +.. _CONFIG_WIFI_PROV_PRIVATE_KEY_PASSWD2: + +CONFIG_WIFI_PROV_PRIVATE_KEY_PASSWD2 + This option specifies the Wi-Fi private key password. + +.. _CONFIG_WIFI_PROV_IDENTITY: + +CONFIG_WIFI_PROV_IDENTITY + This option specifies the Wi-Fi identity. + +.. _CONFIG_WIFI_PROV_PASSWORD: + +CONFIG_WIFI_PROV_PASSWORD + This option specifies the Wi-Fi password. + +.. _CONFIG_WIFI_PROV_VOLATILE_MEMORY: + +CONFIG_WIFI_PROV_VOLATILE_MEMORY + This option specifies the Wi-Fi volatile memory. + +.. _CONFIG_WIFI_PROV_SCAN_BAND: + +CONFIG_WIFI_PROV_SCAN_BAND + This option specifies the Wi-Fi scan band. + +.. _CONFIG_WIFI_PROV_SCAN_PASSIVE: + +CONFIG_WIFI_PROV_SCAN_PASSIVE + This option specifies the Wi-Fi scan passive mode. + +.. _CONFIG_WIFI_PROV_SCAN_PERIOD_MS: + +CONFIG_WIFI_PROV_SCAN_PERIOD_MS + This option specifies the Wi-Fi scan period in milliseconds. + +.. _CONFIG_WIFI_PROV_SCAN_GROUP_CHANNELS: + +CONFIG_WIFI_PROV_SCAN_GROUP_CHANNELS + This option specifies the Wi-Fi scan group channels. + +.. _CONFIG_WIFI_PROV_MAX_DATA_SIZE: + +CONFIG_WIFI_PROV_MAX_DATA_SIZE + This option specifies the maximum protobuf data size. + +.. _CONFIG_WIFI_PROV_MAX_BASE64_SIZE: + +CONFIG_WIFI_PROV_MAX_BASE64_SIZE + This option specifies the maximum base64 input size. + +.. _CONFIG_BT_WIFI_PROV_LOG_LEVEL: + +CONFIG_BT_WIFI_PROV_LOG_LEVEL + This option specifies the log level. + + +Authentication modes +******************** + +The sample supports the following authentication modes: + +* 0: Open +* 1: WEP +* 2: WPA-PSK +* 3: WPA2-PSK +* 4: WPA3-SAE +* 5: WPA3-OWE +* 6: WPA2-Enterprise +* 7: WPA3-Enterprise +* 8: EAP-TLS +* 9: EAP-TTLS +* 10: EAP-PEAP +* 11: EAP-PWD +* 12: EAP-SIM +* 13: EAP-AKA +* 14: EAP-AKA' +* 15: EAP-FAST +* 16: EAP-TEAP +* 17: EAP-PAX +* 18: EAP-PSK +* 19: EAP-SAKE +* 20: EAP-IKEv2 +* 21: EAP-GPSK +* 22: EAP-POTP +* 23: EAP-VENDOR + +Wi-Fi bands +*********** + +The sample can perform Wi-Fi operations across the following bands: + +* 1: 2.4 GHz +* 2: 5 GHz +* 3: 6 GHz + +Shell commands +************** + +The sample provides the following shell commands for testing Wi-Fi provisioning functionality: + +.. list-table:: Shell commands for Wi-Fi provisioning + :header-rows: 1 + + * - Command + - Description + * - ``wifi_prov`` + - Send pregenerated Wi-Fi configuration data to the provisioning service. + * - ``wifi_prov raw `` + - Send raw protobuf-encoded data (Base64 format) to the provisioning service. + * - ``wifi_prov dump_scan `` + - Decode and display scan results in human-readable format from Base64 encoded protobuf data. + * - ``wifi_prov get_status`` + - Send a ``GET_STATUS`` request to the provisioning service. + * - ``wifi_prov start_scan`` + - Send a ``START_SCAN`` request to the provisioning service. + * - ``wifi_prov stop_scan`` + - Send a ``STOP_SCAN`` request to the provisioning service. + * - ``wifi_prov set_config`` + - Send a ``SET_CONFIG`` request with Wi-Fi configuration from Kconfig options. + * - ``wifi_prov forget_config`` + - Send a ``FORGET_CONFIG`` request to the provisioning service. + * - ``wifi_prov info`` + - Display information about the pregenerated Wi-Fi configuration data. + +Building and running +******************** + +.. |sample path| replace:: :file:`samples/wifi/provisioning/internal` + +.. include:: /includes/build_and_run.txt + +1. Configure the sample (optional) by running the following command: + + .. code-block:: bash + + # Edit prj.conf or use west build with -D options + west build -b nrf7002dk/nrf5340/cpuapp samples/wifi/provisioning/internal \ + -- -DCONFIG_WIFI_PROV_SSID="MyNetwork" \ + -DCONFIG_WIFI_PROV_PASSPHRASE="mypassword" + +#. Build the sample by running the following command: + + .. code-block:: bash + + west build -b nrf7002dk/nrf5340/cpuapp samples/wifi/provisioning/internal + +#. Flash the device: + + .. code-block:: bash + + west flash + +#. Connect to console and test: + + .. code-block:: bash + + # Connect to device console + screen /dev/ttyACM0 115200 + + # Test commands + uart:~$ wifi_prov info + uart:~$ wifi_prov get_status + uart:~$ wifi_prov raw 0801 + +Testing +======= + +|test_sample| + +1. |connect_kit| +#. |connect_terminal| + +#. Run the following command to build and flash the sample: + + .. code-block:: bash + + west build -b nrf7002dk/nrf5340/cpuapp samples/wifi/provisioning/internal + west flash + +#. Connect to the device console and test basic commands: + + .. code-block:: text + + uart:~$ wifi_prov info + Wi-Fi Configuration Information: + Size: 156 bytes + Data: 0x20000000 + First 16 bytes: + 0x08 0x04 0x12 0x0a 0x4d 0x79 0x57 0x69 + 0x46 0x69 0x4e 0x65 0x74 0x77 0x6f 0x72 + + uart:~$ wifi_prov get_status + Getting Wi-Fi provisioning status... + === Wi-Fi Provisioning Request === + Type: GET_STATUS + =============================== + Wi-Fi status request sent successfully + +Advanced testing +================ + +Run the following command to test custom protobuf messages: + +.. code-block:: text + + uart:~$ wifi_prov raw 0801 + Sending raw binary data to provisioning service... + Binary data size: 2 bytes + === Wi-Fi Provisioning Request === + Type: GET_STATUS + =============================== + Raw data sent successfully + +Protocol testing +================ + +The sample automatically decodes and logs all protobuf messages: + +.. code-block:: text + + uart:~$ wifi_prov start_scan + Starting Wi-Fi scan... + === Wi-Fi Provisioning Request === + Type: START_SCAN + =============================== + Wi-Fi scan started successfully + + # Response will be logged automatically: + === Wi-Fi Provisioning Response === + Scan Result: + Status: 0 + Networks found: 3 + Network 1: + SSID: MyWi-FiNetwork + BSSID: 11:22:33:44:55:66 + RSSI: -45 + Channel: 6 + Auth mode: 3 + +Directory structure +******************* + +.. code-block:: text + + samples/wifi/provisioning/internal/ + ├── CMakeLists.txt # Build configuration + ├── Kconfig # Kconfig options + ├── prj.conf # Project configuration + ├── README.rst # This documentation + └── src/ + ├── main.c # Application entry point + ├── prov.c # Shell command implementations + └── wifi_prov_transport_stub.c # Transport stub with decoding + +Generated files +**************** + +During the build process, the following files are generated: + +* :file:`wifi_config.h` - Header with Wi-Fi configuration data declarations +* :file:`wifi_config.c` - C file with Wi-Fi configuration data definitions +* Protobuf Python files - Generated from the :file:`*.proto` files for configuration. diff --git a/samples/wifi/provisioning/internal/prj.conf b/samples/wifi/provisioning/internal/prj.conf new file mode 100644 index 000000000000..1a46338d83a7 --- /dev/null +++ b/samples/wifi/provisioning/internal/prj.conf @@ -0,0 +1,125 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_WIFI=y +CONFIG_WIFI_NRF70=y +CONFIG_NANOPB=y +CONFIG_WIFI_PROV_CORE=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_WIFI_CREDENTIALS=y +CONFIG_BASE64=y + +# WPA supplicant +CONFIG_WIFI_NM_WPA_SUPPLICANT=y +CONFIG_NET_L2_WIFI_SHELL=y + +# Networking +CONFIG_NETWORKING=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_LOG=y +CONFIG_NET_IPV4=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=y +CONFIG_NET_DHCPV4=y +CONFIG_DNS_RESOLVER=y + +CONFIG_NET_STATISTICS=y +CONFIG_NET_STATISTICS_WIFI=y +CONFIG_NET_STATISTICS_USER_API=y + +CONFIG_NET_PKT_RX_COUNT=8 +CONFIG_NET_PKT_TX_COUNT=8 + +# Below section is the primary contributor to SRAM and is currently +# tuned for performance, but this will be revisited in the future. +CONFIG_NET_BUF_RX_COUNT=16 +CONFIG_NET_BUF_TX_COUNT=16 +CONFIG_NRF70_RX_NUM_BUFS=16 +CONFIG_NRF70_MAX_TX_AGGREGATION=4 +CONFIG_NET_TC_TX_COUNT=1 +CONFIG_NRF_WIFI_DATA_HEAP_SIZE=50000 + +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=4 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5 +CONFIG_NET_MAX_CONTEXTS=5 +CONFIG_NET_CONTEXT_SYNC_RECV=y + +CONFIG_INIT_STACKS=y + +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_SHELL=y + +# Memories +CONFIG_MAIN_STACK_SIZE=5200 +# For Enterprise protobuf is ~10k +CONFIG_SHELL_STACK_SIZE=32768 + +CONFIG_NET_TX_STACK_SIZE=4096 +CONFIG_NET_RX_STACK_SIZE=4096 + +# Debugging +CONFIG_STACK_SENTINEL=y +CONFIG_DEBUG_COREDUMP=y +CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING=y +CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN=y +CONFIG_SHELL_CMDS_RESIZE=n +#CONFIG_DEBUG=y +CONFIG_WIFI_NM_WPA_SUPPLICANT_LOG_LEVEL_INF=y + +# Kernel options +CONFIG_ENTROPY_GENERATOR=y +CONFIG_REBOOT=y + +# Logging +CONFIG_LOG=y +CONFIG_PRINTK=y +CONFIG_SHELL=y +CONFIG_SHELL_GETOPT=y +CONFIG_DEVICE_SHELL=y +CONFIG_POSIX_TIMERS=y +CONFIG_DATE_SHELL=y +CONFIG_NET_CONFIG_AUTO_INIT=n +CONFIG_POSIX_API=y + +CONFIG_WIFI_CREDENTIALS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y +CONFIG_SETTINGS=y +CONFIG_SETTINGS_NVS=y + +# printing of scan results puts pressure on queues in new locking +# design in net_mgmt. So, use a higher timeout for a crowded +# environment. +CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000 +CONFIG_NET_SOCKETS_POLL_MAX=12 +CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=16 + +# UART and SHELL configurations for large protobuf data (16000 bytes) +CONFIG_SERIAL=y +CONFIG_UART_CONSOLE=y +CONFIG_UART_USE_RUNTIME_CONFIGURE=y +CONFIG_UART_LINE_CTRL=y + +# Increase SHELL buffer sizes for large protobuf data +CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE=16384 +CONFIG_SHELL_BACKEND_SERIAL_TX_RING_BUFFER_SIZE=16384 + +# Increase SHELL command buffer size for large protobuf data +CONFIG_SHELL_CMD_BUFF_SIZE=16384 + +# Increase SHELL history buffer for large commands +CONFIG_SHELL_HISTORY_BUFFER=8192 + +# Enable SHELL features for large data handling +CONFIG_SHELL_GETOPT=y +CONFIG_SHELL_HELP=y +CONFIG_SHELL_HELP_ON_WRONG_ARGUMENT_COUNT=y +CONFIG_SHELL_METAKEYS=y +CONFIG_SHELL_TAB=y +CONFIG_SHELL_TAB_AUTOCOMPLETION=y +CONFIG_SHELL_WILDCARD=y +CONFIG_SHELL_CMDS_RESIZE=n diff --git a/samples/wifi/provisioning/internal/sample.yaml b/samples/wifi/provisioning/internal/sample.yaml new file mode 100644 index 000000000000..46e39fbc3eab --- /dev/null +++ b/samples/wifi/provisioning/internal/sample.yaml @@ -0,0 +1,72 @@ +sample: + description: Internal Wi-Fi provision sample + name: Internal Wi-Fi provision +tests: + sample.nrf7002.int-wifi-provision: + sysbuild: true + build_only: true + integration_platforms: + - nrf7002dk/nrf5340/cpuapp + platform_allow: nrf7002dk/nrf5340/cpuapp + tags: + - ci_build + - sysbuild + - ci_samples_wifi + sample.nrf7001.int-wifi-provision: + sysbuild: true + build_only: true + integration_platforms: + - nrf7002dk/nrf5340/cpuapp/nrf7001 + platform_allow: nrf7002dk/nrf5340/cpuapp/nrf7001 + tags: + - ci_build + - sysbuild + - ci_samples_wifi + skip: true + sample.nrf7002_eks.int-wifi-provision: + sysbuild: true + build_only: true + extra_args: SHIELD=nrf7002ek + integration_platforms: + - nrf5340dk/nrf5340/cpuapp + platform_allow: nrf5340dk/nrf5340/cpuapp + tags: + - ci_build + - sysbuild + - ci_samples_wifi + sample.nrf7001_eks.int-wifi-provision: + sysbuild: true + build_only: true + extra_args: SHIELD=nrf7002ek_nrf7001 + integration_platforms: + - nrf5340dk/nrf5340/cpuapp + platform_allow: nrf5340dk/nrf5340/cpuapp + tags: + - ci_build + - sysbuild + - ci_samples_wifi + sample.nrf7002_eb.thingy53.int-wifi-provision: + sysbuild: true + build_only: true + extra_args: ble_SHIELD=nrf7002eb + integration_platforms: + - thingy53/nrf5340/cpuapp + platform_allow: thingy53/nrf5340/cpuapp + tags: + - ci_build + - sysbuild + - ci_samples_wifi + sample.nrf7002eb2.nrf54h20.int-wifi-provision: + sysbuild: true + build_only: true + extra_args: + - ble_SHIELD="nrf7002eb2" + - SNIPPET=nrf70-wifi + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp + platform_allow: + - nrf54h20dk/nrf54h20/cpuapp + tags: + - ci_build + - sysbuild + - ci_samples_wifi diff --git a/samples/wifi/provisioning/internal/src/main.c b/samples/wifi/provisioning/internal/src/main.c new file mode 100644 index 000000000000..3a88bc8a3c83 --- /dev/null +++ b/samples/wifi/provisioning/internal/src/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(wifi_prov_internal, CONFIG_LOG_DEFAULT_LEVEL); + +int main(void) +{ + LOG_INF("WiFi Provisioning Internal Sample"); + LOG_INF("This sample demonstrates the core WiFi provisioning library functionality"); + +#if defined(CONFIG_WIFI_PROV_CONFIG) + /* Initialize the WiFi provisioning service */ + int ret; + + ret = wifi_prov_init(); + if (ret < 0) { + LOG_ERR("Failed to initialize WiFi provisioning service: %d", ret); + return -1; + } + LOG_INF("WiFi provisioning service initialized successfully"); +#endif + + return 0; +} diff --git a/samples/wifi/provisioning/internal/src/prov.c b/samples/wifi/provisioning/internal/src/prov.c new file mode 100644 index 000000000000..a985037e5178 --- /dev/null +++ b/samples/wifi/provisioning/internal/src/prov.c @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* Include nanopb headers */ +#include "pb_decode.h" +#include "pb_encode.h" + +/* Include generated protobuf headers */ +#include "request.pb.h" +#include "response.pb.h" +#include "result.pb.h" +#include "common.pb.h" + +LOG_MODULE_REGISTER(prov, CONFIG_WIFI_PROV_INTERNAL_LOG_LEVEL); + +/* Generated Wi-Fi configuration data */ +static const uint8_t wifi_config_data[] = { +#include "wifi_config.inc" +}; + +static const size_t wifi_config_data_len = sizeof(wifi_config_data); + +/* Static buffer for requests */ +static struct net_buf_simple req_buf_static; +static struct net_buf_simple *req_buf; + +/** + * @brief Convert base64 string to binary data using Zephyr's base64_decode + */ +static int base64_decode_string(const char *base64_str, uint8_t **binary, size_t *binary_len) +{ + size_t len = strlen(base64_str); + size_t decoded_len = 0; + uint8_t *decoded_data; + int ret; + + /* Check input length */ + if (len > CONFIG_WIFI_PROV_MAX_BASE64_SIZE) { + return -EINVAL; + } + + /* Calculate decoded size (base64 is 4 chars per 3 bytes) */ + decoded_len = (len * 3) / 4; + if (decoded_len > CONFIG_WIFI_PROV_MAX_DATA_SIZE) { + return -EINVAL; + } + + /* Allocate memory for decoded data */ + decoded_data = malloc(decoded_len); + if (!decoded_data) { + return -ENOMEM; + } + + /* Use Zephyr's base64_decode function */ + ret = base64_decode(decoded_data, decoded_len, binary_len, + (const uint8_t *)base64_str, len); + if (ret < 0) { + free(decoded_data); + return ret; + } + + *binary = decoded_data; + return 0; +} + +/** + * @brief Decode and log Wi-Fi provisioning request + */ +static void decode_request(const uint8_t *data, size_t len) +{ + Request request = Request_init_zero; + pb_istream_t stream; + + stream = pb_istream_from_buffer(data, len); + + bool decode_result = pb_decode(&stream, Request_fields, &request); + + if (decode_result) { + LOG_INF("=== Wi-Fi Provisioning Request ==="); + LOG_INF("Operation: %d", request.op_code); + + switch (request.op_code) { + case OpCode_GET_STATUS: + LOG_INF("Type: GET_STATUS"); + break; + case OpCode_START_SCAN: + LOG_INF("Type: START_SCAN"); + break; + case OpCode_STOP_SCAN: + LOG_INF("Type: STOP_SCAN"); + break; + case OpCode_SET_CONFIG: + LOG_INF("Type: SET_CONFIG"); + if (request.has_config) { + LOG_INF(" SSID: %.*s", (int)request.config.wifi.ssid.size, + request.config.wifi.ssid.bytes); + LOG_INF(" BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + request.config.wifi.bssid.bytes[0], + request.config.wifi.bssid.bytes[1], + request.config.wifi.bssid.bytes[2], + request.config.wifi.bssid.bytes[3], + request.config.wifi.bssid.bytes[4], + request.config.wifi.bssid.bytes[5]); + LOG_INF(" Channel: %d", request.config.wifi.channel); + LOG_INF(" Auth mode: %d", request.config.wifi.auth); + if (request.config.has_passphrase) { + LOG_INF(" Passphrase length: %d", + (int)request.config.passphrase.size); + } + } + break; + case OpCode_FORGET_CONFIG: + LOG_INF("Type: FORGET_CONFIG"); + break; + default: + LOG_INF("Type: UNKNOWN (%d)", request.op_code); + break; + } + LOG_INF("==============================="); + } else { + LOG_ERR("Failed to decode request: %s", PB_GET_ERROR(&stream)); + LOG_ERR("pb_decode returned: %s", decode_result ? "true" : "false"); + LOG_ERR("Data length: %d bytes", len); + LOG_HEXDUMP_ERR(data, len, "Raw request data:"); + } +} + +/** + * @brief Decode and display scan results in human-readable format + */ +static void decode_scan_results(const uint8_t *data, size_t len) +{ + /* Try to decode as a single ScanRecord first */ + ScanRecord scan_record = ScanRecord_init_zero; + pb_istream_t stream; + + stream = pb_istream_from_buffer(data, len); + + bool decode_result = pb_decode(&stream, ScanRecord_fields, &scan_record); + + if (decode_result) { + LOG_INF("=== Wi-Fi Scan Record ==="); + + if (scan_record.has_wifi) { + const WifiInfo *wifi = &scan_record.wifi; + + /* SSID */ + if (wifi->ssid.size > 0) { + LOG_INF("SSID: %.*s", (int)wifi->ssid.size, + wifi->ssid.bytes); + } else { + LOG_INF("SSID: "); + } + + /* BSSID */ + if (wifi->bssid.size == 6) { + LOG_INF("BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + wifi->bssid.bytes[0], + wifi->bssid.bytes[1], + wifi->bssid.bytes[2], + wifi->bssid.bytes[3], + wifi->bssid.bytes[4], + wifi->bssid.bytes[5]); + } + + /* Channel */ + LOG_INF("Channel: %d", wifi->channel); + + /* Band */ + if (wifi->has_band) { + const char *band_str = "UNKNOWN"; + + switch (wifi->band) { + case Band_BAND_ANY: + band_str = "ANY"; + break; + case Band_BAND_2_4_GH: + band_str = "2.4 GHz"; + break; + case Band_BAND_5_GH: + band_str = "5 GHz"; + break; + } + LOG_INF("Band: %s", band_str); + } + + /* Auth Mode */ + if (wifi->has_auth) { + const char *auth_str = "UNKNOWN"; + + switch (wifi->auth) { + case AuthMode_OPEN: + auth_str = "OPEN"; + break; + case AuthMode_WEP: + auth_str = "WEP"; + break; + case AuthMode_WPA_PSK: + auth_str = "WPA_PSK"; + break; + case AuthMode_WPA2_PSK: + auth_str = "WPA2_PSK"; + break; + case AuthMode_WPA_WPA2_PSK: + auth_str = "WPA_WPA2_PSK"; + break; + case AuthMode_WPA2_ENTERPRISE: + auth_str = "WPA2_ENTERPRISE"; + break; + case AuthMode_WPA3_PSK: + auth_str = "WPA3_PSK"; + break; + case AuthMode_NONE: + auth_str = "NONE"; + break; + case AuthMode_PSK: + auth_str = "PSK"; + break; + case AuthMode_PSK_SHA256: + auth_str = "PSK_SHA256"; + break; + case AuthMode_SAE: + auth_str = "SAE"; + break; + default: + auth_str = "UNKNOWN"; + break; + } + LOG_INF("Security: %s", auth_str); + } + } + + /* RSSI */ + if (scan_record.has_rssi) { + LOG_INF("RSSI: %d dBm", scan_record.rssi); + } + + LOG_INF("==============================="); + } else { + /* Try to decode as a Result message */ + stream = pb_istream_from_buffer(data, len); + Result result = Result_init_zero; + + decode_result = pb_decode(&stream, Result_fields, &result); + + if (decode_result && result.has_scan_record) { + LOG_INF("=== Wi-Fi Scan Result ==="); + + const ScanRecord *record = &result.scan_record; + + if (record->has_wifi) { + const WifiInfo *wifi = &record->wifi; + + /* SSID */ + if (wifi->ssid.size > 0) { + LOG_INF("SSID: %.*s", (int)wifi->ssid.size, + wifi->ssid.bytes); + } else { + LOG_INF("SSID: "); + } + + /* BSSID */ + if (wifi->bssid.size == 6) { + LOG_INF("BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + wifi->bssid.bytes[0], + wifi->bssid.bytes[1], + wifi->bssid.bytes[2], + wifi->bssid.bytes[3], + wifi->bssid.bytes[4], + wifi->bssid.bytes[5]); + } + + /* Channel */ + LOG_INF("Channel: %d", wifi->channel); + + /* Band */ + if (wifi->has_band) { + const char *band_str = "UNKNOWN"; + + switch (wifi->band) { + case Band_BAND_ANY: + band_str = "ANY"; + break; + case Band_BAND_2_4_GH: + band_str = "2.4 GHz"; + break; + case Band_BAND_5_GH: + band_str = "5 GHz"; + break; + } + LOG_INF("Band: %s", band_str); + } + + /* Auth Mode */ + if (wifi->has_auth) { + const char *auth_str = "UNKNOWN"; + + switch (wifi->auth) { + case AuthMode_OPEN: + auth_str = "OPEN"; + break; + case AuthMode_WEP: + auth_str = "WEP"; + break; + case AuthMode_WPA_PSK: + auth_str = "WPA_PSK"; + break; + case AuthMode_WPA2_PSK: + auth_str = "WPA2_PSK"; + break; + case AuthMode_WPA_WPA2_PSK: + auth_str = "WPA_WPA2_PSK"; + break; + case AuthMode_WPA2_ENTERPRISE: + auth_str = "WPA2_ENTERPRISE"; + break; + case AuthMode_WPA3_PSK: + auth_str = "WPA3_PSK"; + break; + case AuthMode_NONE: + auth_str = "NONE"; + break; + case AuthMode_PSK: + auth_str = "PSK"; + break; + case AuthMode_PSK_SHA256: + auth_str = "PSK_SHA256"; + break; + case AuthMode_SAE: + auth_str = "SAE"; + break; + default: + auth_str = "UNKNOWN"; + break; + } + LOG_INF("Security: %s", auth_str); + } + } + + /* RSSI */ + if (record->has_rssi) { + LOG_INF("RSSI: %d dBm", record->rssi); + } + + /* Connection State */ + if (result.has_state) { + const char *state_str = "UNKNOWN"; + + switch (result.state) { + case ConnectionState_DISCONNECTED: + state_str = "DISCONNECTED"; + break; + case ConnectionState_AUTHENTICATION: + state_str = "AUTHENTICATION"; + break; + case ConnectionState_ASSOCIATION: + state_str = "ASSOCIATION"; + break; + case ConnectionState_OBTAINING_IP: + state_str = "OBTAINING_IP"; + break; + case ConnectionState_CONNECTED: + state_str = "CONNECTED"; + break; + case ConnectionState_CONNECTION_FAILED: + state_str = "CONNECTION_FAILED"; + break; + } + LOG_INF("Connection State: %s", state_str); + } + + /* Failure Reason */ + if (result.has_reason) { + const char *reason_str = "UNKNOWN"; + + switch (result.reason) { + case ConnectionFailureReason_AUTH_ERROR: + reason_str = "AUTH_ERROR"; + break; + case ConnectionFailureReason_NETWORK_NOT_FOUND: + reason_str = "NETWORK_NOT_FOUND"; + break; + case ConnectionFailureReason_TIMEOUT: + reason_str = "TIMEOUT"; + break; + case ConnectionFailureReason_FAIL_IP: + reason_str = "FAIL_IP"; + break; + case ConnectionFailureReason_FAIL_CONN: + reason_str = "FAIL_CONN"; + break; + } + LOG_INF("Failure Reason: %s", reason_str); + } + + LOG_INF("==============================="); + } else { + LOG_ERR("Failed to decode scan results: %s", PB_GET_ERROR(&stream)); + LOG_ERR("pb_decode returned: %s", decode_result ? "true" : "false"); + LOG_ERR("Data length: %d bytes", len); + LOG_HEXDUMP_ERR(data, len, "Raw scan results data:"); + } + } +} + +/** + * @brief Wi-Fi provisioning shell command handler + * + * This command sends the pre-generated Wi-Fi configuration data + * to the Wi-Fi provisioning service. + */ +static int cmd_wifi_prov(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + + if (argc != 1) { + shell_error(shell, "Usage: wifi_prov"); + return -EINVAL; + } + + shell_info(shell, "Sending Wi-Fi configuration to provisioning service..."); + + /* Allocate buffer for the request */ + req_buf = &req_buf_static; + net_buf_simple_init_with_data(req_buf, + (void *)wifi_config_data, + wifi_config_data_len); + + /* Decode and log the request being sent */ + decode_request(req_buf->data, req_buf->len); + + /* Send the configuration to the Wi-Fi provisioning service */ + ret = wifi_prov_recv_req(req_buf); + if (ret < 0) { + shell_error(shell, "Failed to send Wi-Fi configuration: %d", ret); + return ret; + } + + shell_info(shell, "Wi-Fi configuration sent successfully"); + shell_info(shell, "Configuration size: %d bytes", wifi_config_data_len); + + req_buf = NULL; + + return 0; +} + +/** + * @brief Send raw binary data to Wi-Fi provisioning service + */ +static int cmd_wifi_prov_raw(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + uint8_t *raw_data = NULL; + size_t raw_data_len = 0; + static struct net_buf_simple raw_buf; + + if (argc != 2) { + shell_error(shell, "Usage: wifi_prov_raw "); + shell_error(shell, "Example: wifi_prov_raw " + "CARaLgoaCgpTYW1wbGVXaUZpEgYAESIzRFUYAiAAKAMSDnNhbXBsZXBhc3N3b3JkIAA="); + return -EINVAL; + } + + shell_info(shell, "Sending protobuf-encoded data to provisioning service..."); + + /* Convert protobuf-encoded string to binary */ + ret = base64_decode_string(argv[1], &raw_data, &raw_data_len); + if (ret < 0) { + shell_error(shell, "Invalid protobuf-encoded string format: %d", ret); + return ret; + } + + shell_info(shell, "Binary data size: %d bytes", raw_data_len); + + /* Initialize buffer with the binary data */ + net_buf_simple_init_with_data(&raw_buf, raw_data, raw_data_len); + + /* Decode and log the request being sent */ + decode_request(raw_buf.data, raw_buf.len); + + /* Send the raw data to the Wi-Fi provisioning service */ + ret = wifi_prov_recv_req(&raw_buf); + if (ret < 0) { + shell_error(shell, "Failed to send protobuf data: %d", ret); + free(raw_data); + return ret; + } + + shell_info(shell, "Protobuf data sent successfully"); + + /* Clean up */ + free(raw_data); + + return 0; +} + +/** + * @brief Get Wi-Fi provisioning status + */ +static int cmd_wifi_prov_get_status(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + + if (argc != 1) { + shell_error(shell, "Usage: wifi_prov_get_status"); + return -EINVAL; + } + + shell_info(shell, "Getting Wi-Fi provisioning status..."); + + /* Create a simple GET_STATUS request */ + static uint8_t status_req_data[] = {0x08, 0x01}; /* op_code = GET_STATUS */ + static struct net_buf_simple status_req_buf; + + net_buf_simple_init_with_data(&status_req_buf, status_req_data, sizeof(status_req_data)); + + /* Decode and log the request being sent */ + decode_request(status_req_buf.data, status_req_buf.len); + + ret = wifi_prov_recv_req(&status_req_buf); + if (ret < 0) { + shell_error(shell, "Failed to get Wi-Fi status: %d", ret); + return ret; + } + + shell_info(shell, "Wi-Fi status request sent successfully"); + return 0; +} + +/** + * @brief Start Wi-Fi scan + */ +static int cmd_wifi_prov_start_scan(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + + if (argc != 1) { + shell_error(shell, "Usage: wifi_prov_start_scan"); + return -EINVAL; + } + + shell_info(shell, "Starting Wi-Fi scan..."); + + /* Create a simple START_SCAN request */ + static uint8_t scan_req_data[] = {0x08, 0x02}; /* op_code = START_SCAN */ + static struct net_buf_simple scan_req_buf; + + net_buf_simple_init_with_data(&scan_req_buf, scan_req_data, sizeof(scan_req_data)); + + /* Decode and log the request being sent */ + decode_request(scan_req_buf.data, scan_req_buf.len); + + ret = wifi_prov_recv_req(&scan_req_buf); + if (ret < 0) { + shell_error(shell, "Failed to start Wi-Fi scan: %d", ret); + return ret; + } + + shell_info(shell, "Wi-Fi scan started successfully"); + return 0; +} + +/** + * @brief Stop Wi-Fi scan + */ +static int cmd_wifi_prov_stop_scan(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + + if (argc != 1) { + shell_error(shell, "Usage: wifi_prov_stop_scan"); + return -EINVAL; + } + + shell_info(shell, "Stopping Wi-Fi scan..."); + + /* Create a simple STOP_SCAN request */ + static uint8_t stop_scan_req_data[] = {0x08, 0x03}; /* op_code = STOP_SCAN */ + static struct net_buf_simple stop_scan_req_buf; + + net_buf_simple_init_with_data(&stop_scan_req_buf, stop_scan_req_data, + sizeof(stop_scan_req_data)); + + /* Decode and log the request being sent */ + decode_request(stop_scan_req_buf.data, stop_scan_req_buf.len); + + ret = wifi_prov_recv_req(&stop_scan_req_buf); + if (ret < 0) { + shell_error(shell, "Failed to stop Wi-Fi scan: %d", ret); + return ret; + } + + shell_info(shell, "Wi-Fi scan stopped successfully"); + return 0; +} + +/** + * @brief Set Wi-Fi configuration + */ +static int cmd_wifi_prov_set_config(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + + if (argc != 1) { + shell_error(shell, "Usage: wifi_prov_set_config"); + return -EINVAL; + } + + shell_info(shell, "Setting Wi-Fi configuration..."); + + /* Use the pre-generated Wi-Fi configuration */ + static struct net_buf_simple config_req_buf; + + net_buf_simple_init_with_data(&config_req_buf, + (void *)wifi_config_data, + wifi_config_data_len); + + /* Decode and log the request being sent */ + decode_request(config_req_buf.data, config_req_buf.len); + + ret = wifi_prov_recv_req(&config_req_buf); + if (ret < 0) { + shell_error(shell, "Failed to set Wi-Fi configuration: %d", ret); + return ret; + } + + shell_info(shell, "Wi-Fi configuration set successfully"); + return 0; +} + +/** + * @brief Forget Wi-Fi configuration + */ +static int cmd_wifi_prov_forget_config(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + + if (argc != 1) { + shell_error(shell, "Usage: wifi_prov_forget_config"); + return -EINVAL; + } + + shell_info(shell, "Forgetting Wi-Fi configuration..."); + + /* Create a simple FORGET_CONFIG request */ + static uint8_t forget_req_data[] = {0x08, 0x05}; /* op_code = FORGET_CONFIG */ + static struct net_buf_simple forget_req_buf; + + net_buf_simple_init_with_data(&forget_req_buf, forget_req_data, sizeof(forget_req_data)); + + /* Decode and log the request being sent */ + decode_request(forget_req_buf.data, forget_req_buf.len); + + ret = wifi_prov_recv_req(&forget_req_buf); + if (ret < 0) { + shell_error(shell, "Failed to forget Wi-Fi configuration: %d", ret); + return ret; + } + + shell_info(shell, "Wi-Fi configuration forgotten successfully"); + return 0; +} + +/** + * @brief Dump scan results in human-readable format + */ +static int cmd_wifi_prov_dump_scan(const struct shell *shell, size_t argc, char *argv[]) +{ + int ret; + uint8_t *scan_data = NULL; + size_t scan_data_len = 0; + + if (argc != 2) { + shell_error(shell, "Usage: wifi_prov dump_scan "); + shell_error(shell, "Example: wifi_prov dump_scan " + ""); + return -EINVAL; + } + + shell_info(shell, "Decoding scan results..."); + + /* Convert base64 string to binary */ + ret = base64_decode_string(argv[1], &scan_data, &scan_data_len); + if (ret < 0) { + shell_error(shell, "Invalid base64 format: %d", ret); + return ret; + } + + shell_info(shell, "Scan results data size: %d bytes", scan_data_len); + + /* Decode and display scan results */ + decode_scan_results(scan_data, scan_data_len); + + /* Clean up */ + free(scan_data); + + return 0; +} + +/** + * @brief Wi-Fi provisioning status shell command handler + * + * This command shows information about the generated Wi-Fi configuration. + */ +static int cmd_wifi_prov_info(const struct shell *shell, size_t argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + shell_info(shell, "Wi-Fi Configuration Information:"); + shell_info(shell, " Size: %d bytes", wifi_config_data_len); + shell_info(shell, " Data: %p", wifi_config_data); + + /* Decode and display using decode_request for consistency */ + shell_info(shell, "=== Decoded Wi-Fi Configuration ==="); + decode_request(wifi_config_data, wifi_config_data_len); + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(wifi_prov_subcmds, + SHELL_CMD(info, NULL, "Show Wi-Fi configuration information", cmd_wifi_prov_info), + SHELL_CMD(get_status, NULL, "Get Wi-Fi provisioning status", cmd_wifi_prov_get_status), + SHELL_CMD(start_scan, NULL, "Start Wi-Fi scan", cmd_wifi_prov_start_scan), + SHELL_CMD(stop_scan, NULL, "Stop Wi-Fi scan", + cmd_wifi_prov_stop_scan), + SHELL_CMD(set_config, NULL, "Set Wi-Fi configuration", cmd_wifi_prov_set_config), + SHELL_CMD(forget_config, NULL, "Forget Wi-Fi configuration", cmd_wifi_prov_forget_config), + SHELL_CMD(raw, NULL, "Send raw binary data (hex format)", cmd_wifi_prov_raw), + SHELL_CMD(dump_scan, NULL, + "Decode and display scan results (Base64)", cmd_wifi_prov_dump_scan), + SHELL_SUBCMD_SET_END +); + +SHELL_CMD_REGISTER(wifi_prov, &wifi_prov_subcmds, "Wi-Fi provisioning commands", cmd_wifi_prov); diff --git a/samples/wifi/provisioning/internal/src/wifi_prov_transport_stub.c b/samples/wifi/provisioning/internal/src/wifi_prov_transport_stub.c new file mode 100644 index 000000000000..be8cfc35719b --- /dev/null +++ b/samples/wifi/provisioning/internal/src/wifi_prov_transport_stub.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include + +/* Include nanopb headers */ +#include "pb_decode.h" +#include "pb_encode.h" + +/* Include generated protobuf headers */ +#include "request.pb.h" +#include "response.pb.h" +#include "result.pb.h" +#include "common.pb.h" + +LOG_MODULE_REGISTER(wifi_prov_transport, CONFIG_WIFI_PROV_INTERNAL_LOG_LEVEL); + +/** + * @brief Decode and log Wi-Fi provisioning response + */ +static void decode_response(const uint8_t *data, size_t len) +{ + Response response = Response_init_zero; + pb_istream_t stream = pb_istream_from_buffer(data, len); + + bool _decode_result = pb_decode(&stream, Response_fields, &response); + + if (_decode_result) { + LOG_INF("=== Wi-Fi Provisioning Response ==="); + + if (response.has_request_op_code) { + LOG_INF("Request OpCode: %d", response.request_op_code); + } + + if (response.has_status) { + LOG_INF("Status: %d", response.status); + } + + /* Log simple responses even without device_status */ + LOG_INF("Response decoded successfully"); + + if (response.has_device_status) { + LOG_INF("Device Status:"); + if (response.device_status.has_state) { + LOG_INF(" Connection State: %d", response.device_status.state); + } + + if (response.device_status.has_provisioning_info) { + LOG_INF(" Provisioning Info:"); + LOG_INF(" SSID: %.*s", + (int)response.device_status.provisioning_info.ssid.size, + response.device_status.provisioning_info.ssid.bytes); + LOG_INF(" BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + response.device_status.provisioning_info.bssid.bytes[0], + response.device_status.provisioning_info.bssid.bytes[1], + response.device_status.provisioning_info.bssid.bytes[2], + response.device_status.provisioning_info.bssid.bytes[3], + response.device_status.provisioning_info.bssid.bytes[4], + response.device_status.provisioning_info.bssid.bytes[5]); + LOG_INF(" Channel: %d", + response.device_status.provisioning_info.channel); + LOG_INF(" Auth: %d", + response.device_status.provisioning_info.auth); + } + + if (response.device_status.has_connection_info) { + LOG_INF(" Connection Info:"); + LOG_INF(" IP4 Addr: %02x:%02x:%02x:%02x", + response.device_status.connection_info.ip4_addr.bytes[0], + response.device_status.connection_info.ip4_addr.bytes[1], + response.device_status.connection_info.ip4_addr.bytes[2], + response.device_status.connection_info.ip4_addr.bytes[3]); + } + + if (response.device_status.has_scan_info) { + LOG_INF(" Scan Info:"); + LOG_INF(" Band: %d", response.device_status.scan_info.band); + LOG_INF(" Passive: %s", + response.device_status.scan_info.passive ? + "true" : "false"); + LOG_INF(" Period (ms): %d", + response.device_status.scan_info.period_ms); + LOG_INF(" Group Channels: %d", + response.device_status.scan_info.group_channels); + } + + LOG_INF("================================="); + } + } else { + LOG_ERR("Failed to decode response: %s", PB_GET_ERROR(&stream)); + LOG_ERR("pb_decode returned: %s", _decode_result ? "true" : "false"); + LOG_ERR("Data length: %d bytes", len); + LOG_HEXDUMP_ERR(data, len, "Raw response data:"); + } +} + +/** + * @brief Decode and log Wi-Fi provisioning result + */ +static void decode_result(const uint8_t *data, size_t len) +{ + Result result = Result_init_zero; + pb_istream_t stream = pb_istream_from_buffer(data, len); + + bool _decode_result = pb_decode(&stream, Result_fields, &result); + + if (_decode_result) { + LOG_INF("=== Wi-Fi Provisioning Result ==="); + + if (result.has_state) { + LOG_INF("Connection State: %d", result.state); + } + + if (result.has_reason) { + LOG_INF("Failure Reason: %d", result.reason); + } + + if (result.has_scan_record) { + LOG_INF("Scan Record:"); + if (result.scan_record.has_wifi) { + LOG_INF(" SSID: %.*s", + (int)result.scan_record.wifi.ssid.size, + result.scan_record.wifi.ssid.bytes); + LOG_INF(" BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + result.scan_record.wifi.bssid.bytes[0], + result.scan_record.wifi.bssid.bytes[1], + result.scan_record.wifi.bssid.bytes[2], + result.scan_record.wifi.bssid.bytes[3], + result.scan_record.wifi.bssid.bytes[4], + result.scan_record.wifi.bssid.bytes[5]); + LOG_INF(" Channel: %d", result.scan_record.wifi.channel); + LOG_INF(" Auth: %d", result.scan_record.wifi.auth); + } + + if (result.scan_record.has_rssi) { + LOG_INF(" RSSI: %d", result.scan_record.rssi); + } + } + + LOG_INF("==============================="); + } else { + LOG_ERR("Failed to decode result: %s", PB_GET_ERROR(&stream)); + LOG_ERR("pb_decode returned: %s", _decode_result ? "true" : "false"); + LOG_ERR("Data length: %d bytes", len); + LOG_HEXDUMP_ERR(data, len, "Raw result data:"); + } +} + +int wifi_prov_send_rsp(struct net_buf_simple *rsp) +{ + LOG_INF("Wi-Fi provisioning response sent (stub): %d bytes", rsp->len); + + if (rsp->len > 0) { + decode_response(rsp->data, rsp->len); + } + + return 0; +} + +int wifi_prov_send_result(struct net_buf_simple *result) +{ + LOG_INF("Wi-Fi provisioning result sent (stub): %d bytes", result->len); + + if (result->len > 0) { + decode_result(result->data, result->len); + } + + return 0; +} diff --git a/samples/wifi/provisioning/internal/sysbuild.conf b/samples/wifi/provisioning/internal/sysbuild.conf new file mode 100644 index 000000000000..242c0d86c4a7 --- /dev/null +++ b/samples/wifi/provisioning/internal/sysbuild.conf @@ -0,0 +1,7 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +SB_CONFIG_WIFI_NRF70=y diff --git a/subsys/bluetooth/services/wifi_prov/CMakeLists.txt b/subsys/bluetooth/services/wifi_prov/CMakeLists.txt index f28a6040e31f..abf67c6a2717 100644 --- a/subsys/bluetooth/services/wifi_prov/CMakeLists.txt +++ b/subsys/bluetooth/services/wifi_prov/CMakeLists.txt @@ -3,25 +3,19 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -zephyr_library() -set(PROTOC_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}") -set(nanopb_BUILD_RUNTIME OFF) -set(CMAKE_MODULE_PATH ${ZEPHYR_NANOPB_MODULE_DIR}/extra) -find_package(Nanopb REQUIRED) -set(NANOPB_GENERATE_CPP_STANDALONE FALSE) -nanopb_generate_cpp(proto_sources proto_headers - proto/common.proto - proto/version.proto - proto/result.proto - proto/request.proto - proto/response.proto -) +# BLE transport layer for WiFi provisioning +zephyr_library_named(wifi_prov_ble) # Add include path to generated .pb.h header files zephyr_library_include_directories(${CMAKE_CURRENT_BINARY_DIR}) +zephyr_library_include_directories(${ZEPHYR_NRF_MODULE_DIR}/subsys/net/lib/wifi_prov_core) +zephyr_library_include_directories($) zephyr_library_sources( - wifi_prov_ble.c - wifi_prov_handler.c - ${proto_sources}) + wifi_prov_ble.c +) + +zephyr_library_link_libraries(wifi_prov_core) +# Ensure wifi_prov_ble depends on wifi_prov_core +add_dependencies(wifi_prov_ble wifi_prov_core) diff --git a/subsys/bluetooth/services/wifi_prov/proto/request.options b/subsys/bluetooth/services/wifi_prov/proto/request.options deleted file mode 100644 index c5b671adc7e9..000000000000 --- a/subsys/bluetooth/services/wifi_prov/proto/request.options +++ /dev/null @@ -1 +0,0 @@ -WifiConfig.passphrase max_size:64 diff --git a/subsys/bluetooth/services/wifi_prov/wifi_prov_ble.c b/subsys/bluetooth/services/wifi_prov/wifi_prov_ble.c index b4d492233554..3611ca25303d 100644 --- a/subsys/bluetooth/services/wifi_prov/wifi_prov_ble.c +++ b/subsys/bluetooth/services/wifi_prov/wifi_prov_ble.c @@ -22,10 +22,9 @@ #include #include +#include #include -#include "wifi_prov_internal.h" - LOG_MODULE_DECLARE(wifi_prov, CONFIG_BT_WIFI_PROV_LOG_LEVEL); #define PROV_SVC_CP_IDX 3 diff --git a/subsys/bluetooth/services/wifi_prov/wifi_prov_internal.h b/subsys/bluetooth/services/wifi_prov/wifi_prov_internal.h deleted file mode 100644 index b59b346b66fe..000000000000 --- a/subsys/bluetooth/services/wifi_prov/wifi_prov_internal.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2022 Nordic Semiconductor ASA - * - * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - */ - -#ifndef WIFI_PROV_INTERNAL_H -#define WIFI_PROV_INTERNAL_H - -#include -#include -#include "request.pb.h" -#include "response.pb.h" -#include "result.pb.h" -#include "version.pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define INFO_MSG_MAX_LENGTH Info_size -#define REQUEST_MSG_MAX_LENGTH Request_size -#define RESPONSE_MSG_MAX_LENGTH Response_size -#define RESULT_MSG_MAX_LENGTH Result_size - -/** - * @brief Get encoded Info message. - * - * @return 0 if Info retrieved successfully, negative error code otherwise. - */ -int wifi_prov_get_info(struct net_buf_simple *info); - -/** - * @brief Handle received Request message. - * - * The Request is message is encoded into a byte stream, and needs to be decoded - * before accessing its field. - * - * @return 0 if message handled successfully, negative error code otherwise. - */ -int wifi_prov_recv_req(struct net_buf_simple *req); - -/** - * @brief Send Response message. - * - * The Response message is encoded into a byte stream. - * - * @return 0 if message sent successfully, negative error code otherwise. - */ -int wifi_prov_send_rsp(struct net_buf_simple *rsp); - -/** - * @brief Send Result message. - * - * The Result message is encoded into a byte stream. - * - * @return 0 if message sent successfully, negative error code otherwise. - */ -int wifi_prov_send_result(struct net_buf_simple *result); - -#ifdef __cplusplus -} -#endif - -#endif /* WIFI_PROV_INTERNAL_H */ diff --git a/subsys/net/lib/CMakeLists.txt b/subsys/net/lib/CMakeLists.txt index 255a60142af9..3ee325771ae7 100644 --- a/subsys/net/lib/CMakeLists.txt +++ b/subsys/net/lib/CMakeLists.txt @@ -37,3 +37,4 @@ add_subdirectory_ifdef(CONFIG_NRF_PROVISIONING nrf_provisioning) add_subdirectory_ifdef(CONFIG_NRF_MCUMGR_SMP_CLIENT mcumgr_smp_client) add_subdirectory_ifdef(CONFIG_WIFI_NRF70 nrf70_fw_ext) add_subdirectory_ifdef(CONFIG_WIFI_NM_WPA_SUPPLICANT hostap_crypto) +add_subdirectory_ifdef(CONFIG_WIFI_PROV_CORE wifi_prov_core) diff --git a/subsys/net/lib/Kconfig b/subsys/net/lib/Kconfig index b13126f68884..90af07a346dc 100644 --- a/subsys/net/lib/Kconfig +++ b/subsys/net/lib/Kconfig @@ -49,5 +49,6 @@ rsource "nrf_provisioning/Kconfig" rsource "mcumgr_smp_client/Kconfig" rsource "nrf70_fw_ext/Kconfig" rsource "hostap_crypto/Kconfig" +rsource "wifi_prov_core/Kconfig" endmenu diff --git a/subsys/net/lib/wifi_prov_core/CMakeLists.txt b/subsys/net/lib/wifi_prov_core/CMakeLists.txt new file mode 100644 index 000000000000..98c0bbe98037 --- /dev/null +++ b/subsys/net/lib/wifi_prov_core/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_library_named(wifi_prov_core) + +set(PROTOC_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}") +set(nanopb_BUILD_RUNTIME OFF) +set(CMAKE_MODULE_PATH ${ZEPHYR_NANOPB_MODULE_DIR}/extra) +find_package(Nanopb REQUIRED) +set(NANOPB_GENERATE_CPP_STANDALONE FALSE) +nanopb_generate_cpp(proto_sources proto_headers + proto/common.proto + proto/version.proto + proto/result.proto + proto/request.proto + proto/response.proto +) + +# Add include path to generated .pb.h header files +zephyr_library_include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +zephyr_library_sources( + wifi_prov_handler.c + ${proto_sources}) diff --git a/subsys/net/lib/wifi_prov_core/Kconfig b/subsys/net/lib/wifi_prov_core/Kconfig new file mode 100644 index 000000000000..4af1c0280d23 --- /dev/null +++ b/subsys/net/lib/wifi_prov_core/Kconfig @@ -0,0 +1,23 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config WIFI_PROV_CORE + bool "Wi-Fi provisioning core library" + select EXPERIMENTAL + depends on WIFI_NM_WPA_SUPPLICANT + depends on NET_MGMT + help + Enable the Wi-Fi provisioning core subsystem that provides + APIs and infrastructure for Wi-Fi provisioning, including + configuration management and event handling. + + This library is required for Wi-Fi provisioning workflows + and should be enabled when using Wi-Fi provisioning features. + +module = WIFI_PROV_CORE +module-dep = LOG +module-str = Log level for sample +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" diff --git a/subsys/net/lib/wifi_prov_core/proto/CMakeLists.txt b/subsys/net/lib/wifi_prov_core/proto/CMakeLists.txt new file mode 100644 index 000000000000..4323e5c349f2 --- /dev/null +++ b/subsys/net/lib/wifi_prov_core/proto/CMakeLists.txt @@ -0,0 +1,46 @@ +# CMakeLists.txt for generating nanopb C definitions +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +# Define protobuf source files +set(PROTO_FILES + request.proto + common.proto + response.proto + result.proto + version.proto +) + +# Create custom target for nanopb generation (for embedded code) +add_custom_target(generate_nanopb ALL + COMMAND ${CMAKE_COMMAND} -E echo "Generating nanopb C definitions..." + COMMAND protoc --nanopb_out=. ${PROTO_FILES} + COMMAND ${CMAKE_COMMAND} -E echo "Generated nanopb files:" + COMMAND ${CMAKE_COMMAND} -E ls -la *.pb.c *.pb.h + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating nanopb C definitions" + VERBATIM +) + +# Create custom target for cleaning +add_custom_target(clean_nanopb + COMMAND ${CMAKE_COMMAND} -E echo "Cleaning generated files..." + COMMAND ${CMAKE_COMMAND} -E remove *.pb.c *.pb.h + COMMAND ${CMAKE_COMMAND} -E echo "Cleaned!" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Cleaning generated nanopb files" +) + +# Create custom target for testing +add_custom_target(test_nanopb + COMMAND ${CMAKE_COMMAND} -E echo "Running auth mode validation tests..." + COMMAND python3 test_auth_modes.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Running nanopb validation tests" +) + +# Add dependencies to the main project if it exists +if(TARGET ${PROJECT_NAME}) + add_dependencies(${PROJECT_NAME} generate_nanopb) +endif() diff --git a/subsys/bluetooth/services/wifi_prov/proto/common.options b/subsys/net/lib/wifi_prov_core/proto/common.options similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/common.options rename to subsys/net/lib/wifi_prov_core/proto/common.options diff --git a/subsys/bluetooth/services/wifi_prov/proto/common.proto b/subsys/net/lib/wifi_prov_core/proto/common.proto similarity index 73% rename from subsys/bluetooth/services/wifi_prov/proto/common.proto rename to subsys/net/lib/wifi_prov_core/proto/common.proto index cea919359f6f..abd2b2a942da 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/common.proto +++ b/subsys/net/lib/wifi_prov_core/proto/common.proto @@ -55,6 +55,24 @@ enum AuthMode { WPA_WPA2_PSK = 4; WPA2_ENTERPRISE = 5; WPA3_PSK = 6; + // New Zephyr WiFi security types (backward compatible) + NONE = 7; + PSK = 8; + PSK_SHA256 = 9; + SAE = 10; + WAPI = 11; + EAP = 12; + WPA_AUTO_PERSONAL = 13; + DPP = 14; + EAP_PEAP_MSCHAPV2 = 15; + EAP_PEAP_GTC = 16; + EAP_TTLS_MSCHAPV2 = 17; + EAP_PEAP_TLS = 18; + FT_PSK = 19; + FT_SAE = 20; + FT_EAP = 21; + FT_EAP_SHA384 = 22; + SAE_EXT_KEY = 23; } message ScanParams { diff --git a/subsys/net/lib/wifi_prov_core/proto/generate_wifi_prov_config.py b/subsys/net/lib/wifi_prov_core/proto/generate_wifi_prov_config.py new file mode 100644 index 000000000000..b27483a2a402 --- /dev/null +++ b/subsys/net/lib/wifi_prov_core/proto/generate_wifi_prov_config.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python3 +""" +Generate EAP-TLS configuration protobuf message from certificate files. + +This script generates WiFi configuration protobuf messages for EAP-TLS or Personal +authentication modes. It reads certificate files from a specified directory and +creates a protobuf message that can be used for WiFi provisioning. + +Usage: python3 generate_eap_tls_config.py [options] + +Copyright (c) 2025 Nordic Semiconductor ASA + +SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +""" + +import os +import sys +import argparse +import base64 + +# Import generated protobuf modules +try: + from request_pb2 import Request, WifiConfig, EnterpriseCertConfig + from common_pb2 import WifiInfo, AuthMode, Band, OpCode +except ImportError: + # Auto-generate Python protobuf files if they don't exist + print("Python protobuf definitions not found. Auto-generating...") + import subprocess + + # Check if protoc is available + try: + subprocess.run(['protoc', '--version'], capture_output=True, check=True) + except (subprocess.CalledProcessError, FileNotFoundError): + print("Error: 'protoc' not found. Please install protobuf compiler.") + sys.exit(1) + + # Generate Python protobuf files + proto_files = ['request.proto', 'common.proto', 'response.proto', 'result.proto', 'version.proto'] + try: + # Get the directory where this script is located + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Run protoc from the script directory where .proto files are located + subprocess.run(['protoc', '--python_out=' + script_dir] + proto_files, + cwd=script_dir, check=True) + print("✅ Generated Python protobuf definitions") + + # Import the generated modules + from request_pb2 import Request, WifiConfig, EnterpriseCertConfig + from common_pb2 import WifiInfo, AuthMode, Band, OpCode + except subprocess.CalledProcessError as e: + print(f"Error generating protobuf definitions: {e}") + sys.exit(1) + +def read_cert_file(file_path): + """Read certificate file and return as bytes. + + Args: + file_path: Path to the certificate file + + Returns: + bytes: Certificate file contents + + Raises: + FileNotFoundError: If certificate file does not exist + IOError: If file cannot be read + """ + if not os.path.exists(file_path): + raise FileNotFoundError(f"Certificate file not found: {file_path}") + + try: + with open(file_path, 'rb') as f: + return f.read() + except IOError as e: + raise IOError(f"Failed to read certificate file {file_path}: {e}") + +def check_required_files(cert_dir): + """Check if all required certificate files exist. + + Args: + cert_dir: Directory containing certificate files + + Returns: + bool: True if all required files exist, False otherwise + """ + required_files = [ + 'ca.pem', + 'client.pem', + 'client-key.pem', + 'ca2.pem', + 'client2.pem', + 'client-key2.pem' + ] + + missing_files = [] + for file in required_files: + file_path = os.path.join(cert_dir, file) + if not os.path.exists(file_path): + missing_files.append(file) + + if missing_files: + print(f"Error: Missing required certificate files: {missing_files}") + return False + + print(f"✅ All required certificate files found in {cert_dir}") + return True + +def generate_wifi_config(ssid, bssid, channel=6, band=1, auth_mode=5, + cert_dir=None, passphrase=None, identity="user@example.com", password="user_password", + private_key_passwd=None, private_key_passwd2=None): + """Generate WiFi configuration protobuf message (EAP-TLS or Personal).""" + + try: + # Validate auth mode + valid_auth_modes = list(range(24)) # 0-23 based on AuthMode enum + if auth_mode not in valid_auth_modes: + raise ValueError(f"invalid enumerator {auth_mode}") + + # Create WifiInfo + wifi_info = WifiInfo() + wifi_info.ssid = ssid.encode('utf-8') + wifi_info.bssid = bytes.fromhex(bssid.replace(':', '')) + wifi_info.band = Band.BAND_2_4_GH if band == 1 else Band.BAND_5_GH + wifi_info.channel = channel + + # Set auth mode using enum value + try: + wifi_info.auth = auth_mode + except ValueError: + # Try using the enum directly + auth_enum_map = { + 0: AuthMode.OPEN, + 1: AuthMode.WEP, + 2: AuthMode.WPA_PSK, + 3: AuthMode.WPA2_PSK, + 4: AuthMode.WPA_WPA2_PSK, + 5: AuthMode.WPA2_ENTERPRISE, + 6: AuthMode.WPA3_PSK, + 7: AuthMode.NONE, + 8: AuthMode.PSK, + 9: AuthMode.PSK_SHA256, + 10: AuthMode.SAE, + 11: AuthMode.WAPI, + 12: AuthMode.EAP, + 13: AuthMode.WPA_AUTO_PERSONAL, + 14: AuthMode.DPP, + 15: AuthMode.EAP_PEAP_MSCHAPV2, + 16: AuthMode.EAP_PEAP_GTC, + 17: AuthMode.EAP_TTLS_MSCHAPV2, + 18: AuthMode.EAP_PEAP_TLS, + 19: AuthMode.FT_PSK, + 20: AuthMode.FT_SAE, + 21: AuthMode.FT_EAP, + 22: AuthMode.FT_EAP_SHA384, + 23: AuthMode.SAE_EXT_KEY + } + if auth_mode in auth_enum_map: + wifi_info.auth = auth_enum_map[auth_mode] + else: + raise ValueError(f"invalid enumerator {auth_mode}") + + # Create WifiConfig + wifi_config = WifiConfig() + wifi_config.wifi.CopyFrom(wifi_info) + wifi_config.volatileMemory = False + + # Handle EAP-TLS mode + if cert_dir is not None: + if not check_required_files(cert_dir): + return None + + # Read certificate files from directory + ca_cert_data = read_cert_file(os.path.join(cert_dir, 'ca.pem')) + client_cert_data = read_cert_file(os.path.join(cert_dir, 'client.pem')) + private_key_data = read_cert_file(os.path.join(cert_dir, 'client-key.pem')) + + # Read secondary certificates (same as primary for EAP-TLS) + ca_cert2_data = read_cert_file(os.path.join(cert_dir, 'ca2.pem')) + client_cert2_data = read_cert_file(os.path.join(cert_dir, 'client2.pem')) + private_key2_data = read_cert_file(os.path.join(cert_dir, 'client-key2.pem')) + + print("✅ Successfully read all certificate files") + + # Create EnterpriseCertConfig + # MbedTLS uses null-terminator to distinguis b/w PEM and DER formats + def add_null_terminator(data): + if data and not data.endswith(b'\0'): + return data + b'\0' + return data + + cert_config = EnterpriseCertConfig() + cert_config.ca_cert = add_null_terminator(ca_cert_data) + cert_config.client_cert = add_null_terminator(client_cert_data) + cert_config.private_key = add_null_terminator(private_key_data) + cert_config.private_key_passwd = private_key_passwd or password + cert_config.ca_cert2 = add_null_terminator(ca_cert2_data) + cert_config.client_cert2 = add_null_terminator(client_cert2_data) + cert_config.private_key2 = add_null_terminator(private_key2_data) + cert_config.private_key_passwd2 = private_key_passwd2 or password + cert_config.identity = identity + cert_config.password = password + + wifi_config.certs.CopyFrom(cert_config) + print("✅ EAP-TLS mode configured") + + # Handle Personal mode + elif passphrase is not None: + wifi_config.passphrase = passphrase.encode('utf-8') + print("✅ Personal mode configured") + + else: + print("❌ Error: Must specify either --cert-dir (EAP-TLS) or --passphrase (Personal)") + return None + + # Create Request + request = Request() + request.op_code = OpCode.SET_CONFIG + request.config.CopyFrom(wifi_config) + + print("✅ Successfully created protobuf message") + return request + + except ValueError as e: + print(f"❌ Error creating configuration: {e}") + raise # Re-raise ValueError to be caught by caller + except Exception as e: + print(f"❌ Error creating configuration: {e}") + return None + +def main(): + parser = argparse.ArgumentParser( + description='Generate WiFi configuration protobuf message (EAP-TLS or Personal)', + epilog=''' +Examples: + # Generate EAP-TLS configuration (shows only encoded protobuf) + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -d /path/to/certs + + # Generate EAP-TLS with JSON output + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -d /path/to/certs -j + + # Generate EAP-TLS and save as JSON file + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -d /path/to/certs -j -o config.json + + # Generate EAP-TLS and save as binary protobuf + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -d /path/to/certs -o config.bin + + # Generate Personal mode (WPA2-PSK) configuration + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -w "mypassword" -a 3 + + # Generate WPA3-SAE configuration + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -w "mypassword" -a 10 + + # Generate EAP-TLS with custom private key passwords + %(prog)s "MyWiFi" "AA:BB:CC:DD:EE:FF" -d /path/to/certs -k "keypass" -k2 "keypass2" + ''', + formatter_class=argparse.RawDescriptionHelpFormatter, + allow_abbrev=False + ) + parser.add_argument('ssid', help='WiFi SSID') + parser.add_argument('bssid', help='WiFi BSSID (MAC address)') + parser.add_argument('-d', '--cert-dir', help='Directory containing certificate files (for EAP-TLS)') + parser.add_argument('-w', '--passphrase', help='WiFi passphrase (for Personal mode)') + parser.add_argument('-a', '--auth-mode', type=int, default=5, + help='Auth mode: 0=OPEN, 1=WEP, 2=WPA_PSK, 3=WPA2_PSK, 4=WPA_WPA2_PSK, 5=WPA2_ENTERPRISE, 6=WPA3_PSK, 7=NONE, 8=PSK, 9=PSK_SHA256, 10=SAE, 11=WAPI, 12=EAP, 13=WPA_AUTO_PERSONAL, 14=DPP, 15=EAP_PEAP_MSCHAPV2, 16=EAP_PEAP_GTC, 17=EAP_TTLS_MSCHAPV2, 18=EAP_PEAP_TLS, 19=FT_PSK, 20=FT_SAE, 21=FT_EAP, 22=FT_EAP_SHA384, 23=SAE_EXT_KEY (default: 5=WPA2_ENTERPRISE)') + parser.add_argument('-c', '--channel', type=int, default=0, help='WiFi channel (default: 0)') + parser.add_argument('-b', '--band', type=int, default=0, choices=[0, 1, 2], + help='WiFi band: 0=AUTO, 1=2.4GHz, 2=5GHz (default: 0)') + + # Private key password arguments + parser.add_argument('-k', '--private-key-passwd', help='Primary private key password') + parser.add_argument('-k2', '--private-key-passwd2', help='Secondary private key password') + + # EAP identity and password + parser.add_argument('-i', '--identity', default='user@example.com', help='EAP identity') + parser.add_argument('-p', '--password', default='user_password', help='EAP password') + + parser.add_argument('-o', '--output', help='Output file (optional)') + parser.add_argument('-j', '--json', action='store_true', help='Save as JSON file instead of binary protobuf') + + args = parser.parse_args() + + # Validate inputs + if args.cert_dir is not None and not os.path.isdir(args.cert_dir): + print(f"Error: Certificate directory does not exist: {args.cert_dir}") + sys.exit(1) + + if args.cert_dir is None and args.passphrase is None: + print("Error: Must specify either --cert-dir (EAP-TLS) or --passphrase (Personal)") + sys.exit(1) + + # Validate auth mode for EAP-TLS + if args.cert_dir is not None: + if args.auth_mode not in [5, 12, 15, 16, 17, 18]: # Valid EAP modes + print(f"Error: Auth mode {args.auth_mode} is not valid for EAP-TLS") + print("Valid EAP auth modes: 5(WPA2_ENTERPRISE), 12(EAP), 15(EAP_PEAP_MSCHAPV2),") + print("16(EAP_PEAP_GTC), 17(EAP_TTLS_MSCHAPV2), 18(EAP_PEAP_TLS)") + sys.exit(1) + + # Generate configuration + request = generate_wifi_config( + args.ssid, args.bssid, args.channel, args.band, args.auth_mode, + args.cert_dir, args.passphrase, args.identity, args.password, + args.private_key_passwd, args.private_key_passwd2 + ) + + if request is None: + sys.exit(1) + + # Output results + serialized = request.SerializeToString() + + # Show JSON only if requested + if args.json: + import json + from google.protobuf.json_format import MessageToDict + + json_data = MessageToDict(request, preserving_proto_field_name=True) + print("📄 JSON Configuration:") + print(json.dumps(json_data, indent=2)) + print() + + # Always show encoded protobuf string + print("🔧 Encoded Protobuf (Base64):") + print(base64.b64encode(serialized).decode('utf-8')) + print() + + # Save files if output specified + if args.output: + if args.json: + # Save as JSON + import json + from google.protobuf.json_format import MessageToDict + json_data = MessageToDict(request, preserving_proto_field_name=True) + with open(args.output, 'w') as f: + json.dump(json_data, f, indent=2) + print(f"✅ JSON configuration written to: {args.output}") + else: + # Save as binary protobuf + with open(args.output, 'wb') as f: + f.write(serialized) + print(f"✅ Binary protobuf written to: {args.output}") + + print(f"📊 Protobuf size: {len(serialized)} bytes") + + # Get auth mode name based on proto AuthMode enum (backward compatible) + auth_names = { + # Original values (backward compatible) + 0: "OPEN", + 1: "WEP", + 2: "WPA_PSK", + 3: "WPA2_PSK", + 4: "WPA_WPA2_PSK", + 5: "WPA2_ENTERPRISE", + 6: "WPA3_PSK", + # New Zephyr WiFi security types + 7: "NONE", + 8: "PSK", + 9: "PSK_SHA256", + 10: "SAE", + 11: "WAPI", + 12: "EAP", + 13: "WPA_AUTO_PERSONAL", + 14: "DPP", + 15: "EAP_PEAP_MSCHAPV2", + 16: "EAP_PEAP_GTC", + 17: "EAP_TTLS_MSCHAPV2", + 18: "EAP_PEAP_TLS", + 19: "FT_PSK", + 20: "FT_SAE", + 21: "FT_EAP", + 22: "FT_EAP_SHA384", + 23: "SAE_EXT_KEY" + } + auth_name = auth_names.get(args.auth_mode, f"Unknown({args.auth_mode})") + + print(f"\nConfiguration details:") + print(f" SSID: {args.ssid}") + print(f" BSSID: {args.bssid}") + print(f" Channel: {args.channel}") + print(f" Band: {'2.4GHz' if args.band == 1 else '5GHz'}") + print(f" Auth: {auth_name}") + if args.cert_dir: + print(f" Mode: EAP-TLS") + print(f" Identity: {args.identity}") + elif args.passphrase: + print(f" Mode: Personal") + print(f" Passphrase: {'*' * len(args.passphrase)}") + +if __name__ == "__main__": + main() diff --git a/subsys/net/lib/wifi_prov_core/proto/request.options b/subsys/net/lib/wifi_prov_core/proto/request.options new file mode 100644 index 000000000000..c0dde9608fef --- /dev/null +++ b/subsys/net/lib/wifi_prov_core/proto/request.options @@ -0,0 +1,9 @@ +WifiConfig.passphrase max_size:64 +EnterpriseCertConfig.ca_cert max_size:2500 +EnterpriseCertConfig.ca_cert2 max_size:2500 +EnterpriseCertConfig.client_cert max_size:2500 +EnterpriseCertConfig.client_cert2 max_size:2500 +EnterpriseCertConfig.private_key max_size:2500 +EnterpriseCertConfig.private_key2 max_size:2500 +EnterpriseCertConfig.private_key_passwd max_size:32 +EnterpriseCertConfig.private_key_passwd2 max_size:32 diff --git a/subsys/bluetooth/services/wifi_prov/proto/request.proto b/subsys/net/lib/wifi_prov_core/proto/request.proto similarity index 54% rename from subsys/bluetooth/services/wifi_prov/proto/request.proto rename to subsys/net/lib/wifi_prov_core/proto/request.proto index 8d9992601262..a8bdf469248b 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/request.proto +++ b/subsys/net/lib/wifi_prov_core/proto/request.proto @@ -9,11 +9,24 @@ import "common.proto"; option java_multiple_files = true; option java_package = "no.nordicsemi.android.wifi.provisioning"; +message EnterpriseCertConfig { + optional bytes ca_cert = 1; + optional bytes client_cert = 2; + optional bytes private_key = 3; + optional string private_key_passwd = 4; + optional bytes ca_cert2 = 5; + optional bytes client_cert2 = 6; + optional bytes private_key2 = 7; + optional string private_key_passwd2 = 8; + optional string identity = 9; + optional string password = 10; +} message WifiConfig { optional WifiInfo wifi = 1; optional bytes passphrase = 2; optional bool volatileMemory = 3; + optional EnterpriseCertConfig certs = 4; } message Request { diff --git a/subsys/bluetooth/services/wifi_prov/proto/response.options b/subsys/net/lib/wifi_prov_core/proto/response.options similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/response.options rename to subsys/net/lib/wifi_prov_core/proto/response.options diff --git a/subsys/bluetooth/services/wifi_prov/proto/response.proto b/subsys/net/lib/wifi_prov_core/proto/response.proto similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/response.proto rename to subsys/net/lib/wifi_prov_core/proto/response.proto diff --git a/subsys/bluetooth/services/wifi_prov/proto/result.proto b/subsys/net/lib/wifi_prov_core/proto/result.proto similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/result.proto rename to subsys/net/lib/wifi_prov_core/proto/result.proto diff --git a/subsys/net/lib/wifi_prov_core/proto/test_auth_modes.py b/subsys/net/lib/wifi_prov_core/proto/test_auth_modes.py new file mode 100644 index 000000000000..f0024313cb48 --- /dev/null +++ b/subsys/net/lib/wifi_prov_core/proto/test_auth_modes.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +""" +Test script to verify auth mode validation in generate_eap_tls_config.py + +Copyright (c) 2025 Nordic Semiconductor ASA + +SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +""" + +import sys +import os + +# Add current directory to path to import the script +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +try: + from generate_wifi_prov_config import generate_wifi_config +except ImportError as e: + print(f"Error importing generate_wifi_prov_config: {e}") + print("Make sure to run 'make generate' first to create protobuf files") + sys.exit(1) + +def test_auth_mode_validation(): + """Test auth mode validation with various values.""" + + print("Testing auth mode validation...") + + # Test valid auth modes + valid_modes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] + + for mode in valid_modes: + try: + result = generate_wifi_config( + ssid="TestWiFi", + bssid="00:11:22:33:44:55", + auth_mode=mode, + passphrase="testpass" + ) + if result is not None: + print(f"✅ Auth mode {mode} is valid") + else: + print(f"❌ Auth mode {mode} returned None") + except ValueError as e: + print(f"❌ Auth mode {mode} failed: {e}") + except Exception as e: + print(f"❌ Auth mode {mode} unexpected error: {e}") + + # Test invalid auth modes + invalid_modes = [-1, 24, 100, 999] + + for mode in invalid_modes: + try: + result = generate_wifi_config( + ssid="TestWiFi", + bssid="00:11:22:33:44:55", + auth_mode=mode, + passphrase="testpass" + ) + print(f"❌ Auth mode {mode} should have failed but didn't") + except ValueError as e: + if "invalid enumerator" in str(e): + print(f"✅ Auth mode {mode} correctly rejected: {e}") + else: + print(f"❌ Auth mode {mode} unexpected ValueError: {e}") + except Exception as e: + if "invalid enumerator" in str(e): + print(f"✅ Auth mode {mode} correctly rejected: {e}") + else: + print(f"❌ Auth mode {mode} unexpected error: {e}") + +if __name__ == "__main__": + test_auth_mode_validation() diff --git a/subsys/bluetooth/services/wifi_prov/proto/version.options b/subsys/net/lib/wifi_prov_core/proto/version.options similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/version.options rename to subsys/net/lib/wifi_prov_core/proto/version.options diff --git a/subsys/bluetooth/services/wifi_prov/proto/version.proto b/subsys/net/lib/wifi_prov_core/proto/version.proto similarity index 86% rename from subsys/bluetooth/services/wifi_prov/proto/version.proto rename to subsys/net/lib/wifi_prov_core/proto/version.proto index bc36e1f5e070..8db7f8529406 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/version.proto +++ b/subsys/net/lib/wifi_prov_core/proto/version.proto @@ -9,5 +9,5 @@ option java_multiple_files = true; option java_package = "no.nordicsemi.android.wifi.provisioning"; message Info { - required uint32 version = 1; + required uint32 version = 2; } diff --git a/subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c b/subsys/net/lib/wifi_prov_core/wifi_prov_handler.c similarity index 82% rename from subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c rename to subsys/net/lib/wifi_prov_core/wifi_prov_handler.c index efc34eeaf8f1..d94228f51b9c 100644 --- a/subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c +++ b/subsys/net/lib/wifi_prov_core/wifi_prov_handler.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -17,10 +19,20 @@ #include #include -#include -#include "wifi_prov_internal.h" +#include -LOG_MODULE_REGISTER(wifi_prov, CONFIG_BT_WIFI_PROV_LOG_LEVEL); +/* Weak transport functions - can be overridden by transport layer */ +__weak int wifi_prov_send_rsp(struct net_buf_simple *rsp) +{ + return -ENOTSUP; +} + +__weak int wifi_prov_send_result(struct net_buf_simple *result) +{ + return -ENOTSUP; +} + +LOG_MODULE_REGISTER(wifi_prov, CONFIG_WIFI_PROV_CORE_LOG_LEVEL); #define WIFI_PROV_MGMT_EVENTS (NET_EVENT_WIFI_SCAN_RESULT | \ NET_EVENT_WIFI_CONNECT_RESULT) @@ -33,6 +45,18 @@ static struct wifi_credentials_personal config_in_ram; static struct wifi_credentials_personal config_buf; static bool save_config_to_ram; +enum wifi_enterprise_cert_sec_tags { + WIFI_CERT_CA_SEC_TAG = 0x1020001, + WIFI_CERT_CLIENT_KEY_SEC_TAG, + WIFI_CERT_SERVER_KEY_SEC_TAG, + WIFI_CERT_CLIENT_SEC_TAG, + WIFI_CERT_SERVER_SEC_TAG, + /* Phase 2 */ + WIFI_CERT_CA_P2_SEC_TAG, + WIFI_CERT_CLIENT_KEY_P2_SEC_TAG, + WIFI_CERT_CLIENT_P2_SEC_TAG, +}; + /* Configurator should read info before any other operations. * We should define some useful fields to facilicate bootstrapping. */ @@ -238,13 +262,56 @@ static void prov_set_config_handler(Request *req, Response *rsp) config.header.type = WIFI_SECURITY_TYPE_PSK_SHA256; } else if (req->config.wifi.auth == AuthMode_WPA3_PSK) { config.header.type = WIFI_SECURITY_TYPE_SAE; - } else { - config.header.type = WIFI_SECURITY_TYPE_UNKNOWN; } } } else { - /* If no passphrase provided, ignore the auth field and regard it as open */ - config.header.type = WIFI_SECURITY_TYPE_NONE; +#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE) + if (req->config.wifi.auth == AuthMode_WPA2_ENTERPRISE) { + config.header.type = WIFI_SECURITY_TYPE_EAP_TLS; + if (req->config.has_certs == true) { + if (req->config.certs.has_ca_cert) { + tls_credential_add(WIFI_CERT_CA_SEC_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + req->config.certs.ca_cert.bytes, + req->config.certs.ca_cert.size); + } + if (req->config.certs.has_client_cert) { + tls_credential_add(WIFI_CERT_CLIENT_SEC_TAG, + TLS_CREDENTIAL_PUBLIC_CERTIFICATE, + req->config.certs.client_cert.bytes, + req->config.certs.client_cert.size); + } + if (req->config.certs.has_private_key) { + tls_credential_add(WIFI_CERT_CLIENT_KEY_SEC_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, + req->config.certs.private_key.bytes, + req->config.certs.private_key.size); + } + if (req->config.certs.has_ca_cert) { + tls_credential_add(WIFI_CERT_CA_P2_SEC_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + req->config.certs.ca_cert.bytes, + req->config.certs.ca_cert.size); + } + if (req->config.certs.has_client_cert) { + tls_credential_add(WIFI_CERT_CLIENT_P2_SEC_TAG, + TLS_CREDENTIAL_PUBLIC_CERTIFICATE, + req->config.certs.client_cert.bytes, + req->config.certs.client_cert.size); + } + if (req->config.certs.has_private_key) { + tls_credential_add(WIFI_CERT_CLIENT_KEY_P2_SEC_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, + req->config.certs.private_key.bytes, + req->config.certs.private_key.size); + } + } + } else +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */ + { + /* If no passphrase provided, ignore the auth field and regard it as open */ + config.header.type = WIFI_SECURITY_TYPE_NONE; + } } /* Band */ if (req->config.wifi.has_band == true) { @@ -280,7 +347,32 @@ static void prov_set_config_handler(Request *req, Response *rsp) cnx_params.psk_length = 0; cnx_params.sae_password = NULL; cnx_params.sae_password_length = 0; +#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE) + if (config.header.type == WIFI_SECURITY_TYPE_EAP_TLS) { + /* If EAP TLS is used, we need to set the private key password. */ + const EnterpriseCertConfig *config_ptr = &req->config.certs; + + if (config_ptr->has_private_key_passwd) { + cnx_params.key_passwd = config_ptr->private_key_passwd; + cnx_params.key_passwd_length = sizeof(config_ptr->private_key_passwd); + } else { + cnx_params.key_passwd = NULL; + cnx_params.key_passwd_length = 0; + } + if (config_ptr->has_private_key_passwd2) { + cnx_params.key2_passwd = config_ptr->private_key_passwd2; + cnx_params.key2_passwd_length = sizeof(config_ptr->private_key_passwd2); + } else { + cnx_params.key2_passwd = NULL; + cnx_params.key2_passwd_length = 0; + } + if (wifi_set_enterprise_credentials(iface, 0) != 0) { + LOG_ERR("Failed to set enterprise credentials"); + } + } else if (config.header.type != WIFI_SECURITY_TYPE_NONE) { +#else /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */ if (config.header.type != WIFI_SECURITY_TYPE_NONE) { +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */ cnx_params.psk = config.password; cnx_params.psk_length = config.password_len; } @@ -292,6 +384,7 @@ static void prov_set_config_handler(Request *req, Response *rsp) rc = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &cnx_params, sizeof(struct wifi_connect_req_params)); + /* Invalid argument error. */ if (rc == -EINVAL) { rsp->has_status = true; @@ -569,7 +662,7 @@ static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, } } -bool bt_wifi_prov_state_get(void) +bool wifi_prov_state_get(void) { struct wifi_credentials_personal config = { 0 }; @@ -581,7 +674,7 @@ bool bt_wifi_prov_state_get(void) } } -int bt_wifi_prov_init(void) +int wifi_prov_init(void) { net_mgmt_init_event_callback(&wifi_prov_mgmt_cb, wifi_mgmt_event_handler,