From 2102c94f7e496a1ae76bd3a7e95f80dd0fc3dda3 Mon Sep 17 00:00:00 2001 From: Ravi Dondaputi Date: Mon, 28 Jul 2025 22:52:42 +0530 Subject: [PATCH 1/5] provisioning: wifi: Add enterprise security support Add support to configure enterprise mode security and upload corresponding certificates. Signed-off-by: Ravi Dondaputi --- samples/wifi/provisioning/ble/prj.conf | 2 +- .../services/wifi_prov/proto/request.options | 8 ++ .../services/wifi_prov/proto/request.proto | 13 +++ .../services/wifi_prov/proto/version.proto | 2 +- .../services/wifi_prov/wifi_prov_handler.c | 82 ++++++++++++++++++- 5 files changed, 103 insertions(+), 4 deletions(-) diff --git a/samples/wifi/provisioning/ble/prj.conf b/samples/wifi/provisioning/ble/prj.conf index ed114fc4543f..011437df0e77 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 diff --git a/subsys/bluetooth/services/wifi_prov/proto/request.options b/subsys/bluetooth/services/wifi_prov/proto/request.options index c5b671adc7e9..c0dde9608fef 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/request.options +++ b/subsys/bluetooth/services/wifi_prov/proto/request.options @@ -1 +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/bluetooth/services/wifi_prov/proto/request.proto index 8d9992601262..a8bdf469248b 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/request.proto +++ b/subsys/bluetooth/services/wifi_prov/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/version.proto b/subsys/bluetooth/services/wifi_prov/proto/version.proto index bc36e1f5e070..8db7f8529406 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/version.proto +++ b/subsys/bluetooth/services/wifi_prov/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/bluetooth/services/wifi_prov/wifi_prov_handler.c index efc34eeaf8f1..65da461a8882 100644 --- a/subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c +++ b/subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,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,8 +252,48 @@ 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; +#if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE) + } else 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); + } + } +#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */ } } } else { @@ -280,7 +334,31 @@ 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; } From b256f66524dc6ffa2c58751503725faf2e7b7b2b Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 30 Jul 2025 23:44:55 +0530 Subject: [PATCH 2/5] bluetooth: wifi_prov: Update auth mode to match Zephyr enumerations It's easier to use same enumerations across, but we aalso use the existing ones to not break compatibility. Signed-off-by: Chaitanya Tata --- .../services/wifi_prov/proto/common.proto | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/subsys/bluetooth/services/wifi_prov/proto/common.proto b/subsys/bluetooth/services/wifi_prov/proto/common.proto index cea919359f6f..abd2b2a942da 100644 --- a/subsys/bluetooth/services/wifi_prov/proto/common.proto +++ b/subsys/bluetooth/services/wifi_prov/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 { From b70e2761c30f4fa80838f596c607c7a657201dfc Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 30 Jul 2025 23:36:20 +0530 Subject: [PATCH 3/5] bluetooth: wifi_prov: Add tools to generate encoded string To make it easier to send the encoded string for testing add tools: * Makefile - to generate proto definitions in python * Python script - to generate JSON and binary string * Doc - to explain the usage Signed-off-by: Chaitanya Tata --- .../libraries/networking/wifi_prov_tools.rst | 179 ++++++++ .../services/wifi_prov/proto/CMakeLists.txt | 46 +++ .../proto/generate_wifi_prov_config.py | 390 ++++++++++++++++++ .../wifi_prov/proto/test_auth_modes.py | 72 ++++ 4 files changed, 687 insertions(+) create mode 100644 doc/nrf/libraries/networking/wifi_prov_tools.rst create mode 100644 subsys/bluetooth/services/wifi_prov/proto/CMakeLists.txt create mode 100644 subsys/bluetooth/services/wifi_prov/proto/generate_wifi_prov_config.py create mode 100644 subsys/bluetooth/services/wifi_prov/proto/test_auth_modes.py 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/subsys/bluetooth/services/wifi_prov/proto/CMakeLists.txt b/subsys/bluetooth/services/wifi_prov/proto/CMakeLists.txt new file mode 100644 index 000000000000..4323e5c349f2 --- /dev/null +++ b/subsys/bluetooth/services/wifi_prov/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/generate_wifi_prov_config.py b/subsys/bluetooth/services/wifi_prov/proto/generate_wifi_prov_config.py new file mode 100644 index 000000000000..b27483a2a402 --- /dev/null +++ b/subsys/bluetooth/services/wifi_prov/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/bluetooth/services/wifi_prov/proto/test_auth_modes.py b/subsys/bluetooth/services/wifi_prov/proto/test_auth_modes.py new file mode 100644 index 000000000000..f0024313cb48 --- /dev/null +++ b/subsys/bluetooth/services/wifi_prov/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() From 925a70dc5de464127b44a4bac8a84ec5efce7499 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 31 Jul 2025 01:01:02 +0530 Subject: [PATCH 4/5] wifi_prov: Rejig the library to use for multiple transports We now have a new application for this library which will be used with no transport, so, move the the core provisioning stuff to a new library that can be used by BLE or no transport. Signed-off-by: Chaitanya Tata --- CODEOWNERS | 4 +- doc/_utils/redirects.py | 3 +- .../bluetooth/services/wifi_prov_ble.rst | 119 ++++++++++++++++++ .../wifi_prov_core.rst} | 115 +++++------------ .../bluetooth/services/wifi_provisioning.h | 20 --- include/net/wifi_prov_core/wifi_prov_core.h | 91 ++++++++++++++ samples/wifi/provisioning/ble/CMakeLists.txt | 4 + samples/wifi/provisioning/ble/README.rst | 4 +- samples/wifi/provisioning/ble/prj.conf | 1 + samples/wifi/provisioning/ble/src/main.c | 5 +- .../services/wifi_prov/CMakeLists.txt | 26 ++-- .../services/wifi_prov/wifi_prov_ble.c | 3 +- .../services/wifi_prov/wifi_prov_internal.h | 65 ---------- subsys/net/lib/CMakeLists.txt | 1 + subsys/net/lib/Kconfig | 1 + subsys/net/lib/wifi_prov_core/CMakeLists.txt | 27 ++++ subsys/net/lib/wifi_prov_core/Kconfig | 23 ++++ .../lib/wifi_prov_core}/proto/CMakeLists.txt | 0 .../lib/wifi_prov_core}/proto/common.options | 0 .../lib/wifi_prov_core}/proto/common.proto | 0 .../proto/generate_wifi_prov_config.py | 0 .../lib/wifi_prov_core}/proto/request.options | 0 .../lib/wifi_prov_core}/proto/request.proto | 0 .../wifi_prov_core}/proto/response.options | 0 .../lib/wifi_prov_core}/proto/response.proto | 0 .../lib/wifi_prov_core}/proto/result.proto | 0 .../wifi_prov_core}/proto/test_auth_modes.py | 0 .../lib/wifi_prov_core}/proto/version.options | 0 .../lib/wifi_prov_core}/proto/version.proto | 0 .../lib/wifi_prov_core}/wifi_prov_handler.c | 111 +++++++++------- 30 files changed, 382 insertions(+), 241 deletions(-) create mode 100644 doc/nrf/libraries/bluetooth/services/wifi_prov_ble.rst rename doc/nrf/libraries/{bluetooth/services/wifi_prov.rst => networking/wifi_prov_core.rst} (65%) create mode 100644 include/net/wifi_prov_core/wifi_prov_core.h delete mode 100644 subsys/bluetooth/services/wifi_prov/wifi_prov_internal.h create mode 100644 subsys/net/lib/wifi_prov_core/CMakeLists.txt create mode 100644 subsys/net/lib/wifi_prov_core/Kconfig rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/CMakeLists.txt (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/common.options (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/common.proto (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/generate_wifi_prov_config.py (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/request.options (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/request.proto (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/response.options (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/response.proto (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/result.proto (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/test_auth_modes.py (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/version.options (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/proto/version.proto (100%) rename subsys/{bluetooth/services/wifi_prov => net/lib/wifi_prov_core}/wifi_prov_handler.c (90%) 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/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 011437df0e77..fa3b94a54661 100644 --- a/samples/wifi/provisioning/ble/prj.conf +++ b/samples/wifi/provisioning/ble/prj.conf @@ -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/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/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/bluetooth/services/wifi_prov/proto/CMakeLists.txt b/subsys/net/lib/wifi_prov_core/proto/CMakeLists.txt similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/CMakeLists.txt rename to subsys/net/lib/wifi_prov_core/proto/CMakeLists.txt 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 100% rename from subsys/bluetooth/services/wifi_prov/proto/common.proto rename to subsys/net/lib/wifi_prov_core/proto/common.proto diff --git a/subsys/bluetooth/services/wifi_prov/proto/generate_wifi_prov_config.py b/subsys/net/lib/wifi_prov_core/proto/generate_wifi_prov_config.py similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/generate_wifi_prov_config.py rename to subsys/net/lib/wifi_prov_core/proto/generate_wifi_prov_config.py diff --git a/subsys/bluetooth/services/wifi_prov/proto/request.options b/subsys/net/lib/wifi_prov_core/proto/request.options similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/request.options rename to subsys/net/lib/wifi_prov_core/proto/request.options diff --git a/subsys/bluetooth/services/wifi_prov/proto/request.proto b/subsys/net/lib/wifi_prov_core/proto/request.proto similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/request.proto rename to subsys/net/lib/wifi_prov_core/proto/request.proto 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/bluetooth/services/wifi_prov/proto/test_auth_modes.py b/subsys/net/lib/wifi_prov_core/proto/test_auth_modes.py similarity index 100% rename from subsys/bluetooth/services/wifi_prov/proto/test_auth_modes.py rename to subsys/net/lib/wifi_prov_core/proto/test_auth_modes.py 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 100% rename from subsys/bluetooth/services/wifi_prov/proto/version.proto rename to subsys/net/lib/wifi_prov_core/proto/version.proto 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 90% rename from subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c rename to subsys/net/lib/wifi_prov_core/wifi_prov_handler.c index 65da461a8882..d94228f51b9c 100644 --- a/subsys/bluetooth/services/wifi_prov/wifi_prov_handler.c +++ b/subsys/net/lib/wifi_prov_core/wifi_prov_handler.c @@ -19,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) @@ -252,53 +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 { #if defined(CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE) - } else 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); - } + 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); } -#endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE */ } + } 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; } - } else { - /* 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) { @@ -338,6 +351,7 @@ static void prov_set_config_handler(Request *req, Response *rsp) 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); @@ -370,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; @@ -647,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 }; @@ -659,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, From 2de5ed99c0d509458feed412bc2509f660733300 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Thu, 31 Jul 2025 00:06:30 +0530 Subject: [PATCH 5/5] samples: wifi: provisioning: Add new sample with internal transport This is handy to test the core part of provisioning without worrying about BLE or SAP, this doesn't need any external dependencies, but still uses protbuf as the configuration mechanism, relies on Wi-Fi provisioning core library. Signed-off-by: Chaitanya Tata --- .../wifi/provisioning/internal/CMakeLists.txt | 61 ++ samples/wifi/provisioning/internal/Kconfig | 145 ++++ samples/wifi/provisioning/internal/README.rst | 364 +++++++++ samples/wifi/provisioning/internal/prj.conf | 125 +++ .../wifi/provisioning/internal/sample.yaml | 72 ++ samples/wifi/provisioning/internal/src/main.c | 33 + samples/wifi/provisioning/internal/src/prov.c | 747 ++++++++++++++++++ .../internal/src/wifi_prov_transport_stub.c | 174 ++++ .../wifi/provisioning/internal/sysbuild.conf | 7 + 9 files changed, 1728 insertions(+) create mode 100644 samples/wifi/provisioning/internal/CMakeLists.txt create mode 100644 samples/wifi/provisioning/internal/Kconfig create mode 100644 samples/wifi/provisioning/internal/README.rst create mode 100644 samples/wifi/provisioning/internal/prj.conf create mode 100644 samples/wifi/provisioning/internal/sample.yaml create mode 100644 samples/wifi/provisioning/internal/src/main.c create mode 100644 samples/wifi/provisioning/internal/src/prov.c create mode 100644 samples/wifi/provisioning/internal/src/wifi_prov_transport_stub.c create mode 100644 samples/wifi/provisioning/internal/sysbuild.conf 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