net: lib: add wifi_credentials library
Upstream NCS's library for storing Wi-Fi credentials. This library allows storage of Wi-Fi credentials using different backends. Either the Zephyr settings subsystem or the PSA secure backend can be used. For testing purposes, credentials can be defined statically. Signed-off-by: Ravi Dondaputi <ravi.dondaputi@nordicsemi.no> Signed-off-by: Kapil Bhatt <kapil.bhatt@nordicsemi.no> Signed-off-by: Gregers Gram Rygg <gregers.gram.rygg@nordicsemi.no> Signed-off-by: Kaja Koren <kaja.koren@nordicsemi.no> Signed-off-by: Simen S. Røstad <simen.rostad@nordicsemi.no> Signed-off-by: Maximilian Deubel <maximilian.deubel@nordicsemi.no>
This commit is contained in:
parent
b758e205b2
commit
f6d305a529
12 changed files with 1457 additions and 1 deletions
218
include/zephyr/net/wifi_credentials.h
Normal file
218
include/zephyr/net/wifi_credentials.h
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef WIFI_CREDENTIALS_H__
|
||||
#define WIFI_CREDENTIALS_H__
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/net/wifi.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
/**
|
||||
* @defgroup wifi_credentials Wi-Fi credentials library
|
||||
* @ingroup networking
|
||||
* @since 4.0
|
||||
* @version 0.1.0
|
||||
* @{
|
||||
* @brief Library that provides a way to store and load Wi-Fi credentials.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* this entry contains a BSSID */
|
||||
#define WIFI_CREDENTIALS_FLAG_BSSID BIT(0)
|
||||
/* this entry is to be preferred over others */
|
||||
#define WIFI_CREDENTIALS_FLAG_FAVORITE BIT(1)
|
||||
/* this entry can use the 2.4 GHz band */
|
||||
#define WIFI_CREDENTIALS_FLAG_2_4GHz BIT(2)
|
||||
/* this entry can use the 5 GHz band */
|
||||
#define WIFI_CREDENTIALS_FLAG_5GHz BIT(3)
|
||||
/* this entry requires management frame protection */
|
||||
#define WIFI_CREDENTIALS_FLAG_MFP_REQUIRED BIT(4)
|
||||
/* this entry disables management frame protection */
|
||||
#define WIFI_CREDENTIALS_FLAG_MFP_DISABLED BIT(5)
|
||||
|
||||
#define WIFI_CREDENTIALS_MAX_PASSWORD_LEN \
|
||||
MAX(WIFI_PSK_MAX_LEN, CONFIG_WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH)
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi credentials entry header
|
||||
* @note Every settings entry starts with this header.
|
||||
* Depending on the `type` field, the header can be casted to a larger type.
|
||||
* In addition to SSID (usually a string) and BSSID (a MAC address),
|
||||
* a `flags` field can be used to control some detail settings.
|
||||
*
|
||||
*/
|
||||
struct wifi_credentials_header {
|
||||
enum wifi_security_type type; /**< Wi-Fi security type */
|
||||
char ssid[WIFI_SSID_MAX_LEN]; /**< SSID (Service Set Identifier) */
|
||||
size_t ssid_len; /**< Length of the SSID */
|
||||
uint32_t flags; /**< Flags for controlling detail settings */
|
||||
uint32_t timeout; /**< Timeout for connecting to the network */
|
||||
uint8_t bssid[WIFI_MAC_ADDR_LEN]; /**< BSSID (Basic Service Set Identifier) */
|
||||
uint8_t channel; /**< Channel on which the network operates */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi Personal credentials entry
|
||||
* @note Contains only the header and a password.
|
||||
* For PSK security, passwords can be up to `WIFI_PSK_MAX_LEN` bytes long
|
||||
* including NULL termination. For SAE security it can range up to
|
||||
* `CONFIG_WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH`.
|
||||
*
|
||||
*/
|
||||
struct wifi_credentials_personal {
|
||||
struct wifi_credentials_header header; /**< Header */
|
||||
char password[WIFI_CREDENTIALS_MAX_PASSWORD_LEN]; /**< Password/PSK */
|
||||
size_t password_len; /**< Length of the password */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Wi-Fi Enterprise credentials entry
|
||||
* @note This functionality is not yet implemented.
|
||||
*/
|
||||
struct wifi_credentials_enterprise {
|
||||
struct wifi_credentials_header header; /**< Header */
|
||||
size_t identity_len; /**< Length of the identity */
|
||||
size_t anonymous_identity_len; /**< Length of the anonymous identity */
|
||||
size_t password_len; /**< Length of the password */
|
||||
size_t ca_cert_len; /**< Length of the CA certificate */
|
||||
size_t client_cert_len; /**< Length of the client certificate */
|
||||
size_t private_key_len; /**< Length of the private key */
|
||||
size_t private_key_pw_len; /**< Length of the private key password */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get credentials for given SSID.
|
||||
*
|
||||
* @param[in] ssid SSID to look for
|
||||
* @param[in] ssid_len length of SSID
|
||||
* @param[out] type Wi-Fi security type
|
||||
* @param[out] bssid_buf buffer to store BSSID if it was fixed
|
||||
* @param[in] bssid_buf_len length of bssid_buf
|
||||
* @param[out] password_buf buffer to store password
|
||||
* @param[in] password_buf_len length of password_buf
|
||||
* @param[out] password_len length of password
|
||||
* @param[out] flags flags
|
||||
* @param[out] channel channel
|
||||
* @param[out] timeout timeout
|
||||
*
|
||||
* @return 0 Success.
|
||||
* @return -ENOENT No network with this SSID was found.
|
||||
* @return -EINVAL A required buffer was NULL or invalid SSID length.
|
||||
* @return -EPROTO The network with this SSID is not a personal network.
|
||||
*/
|
||||
int wifi_credentials_get_by_ssid_personal(const char *ssid, size_t ssid_len,
|
||||
enum wifi_security_type *type, uint8_t *bssid_buf,
|
||||
size_t bssid_buf_len, char *password_buf,
|
||||
size_t password_buf_len, size_t *password_len,
|
||||
uint32_t *flags, uint8_t *channel, uint32_t *timeout);
|
||||
|
||||
/**
|
||||
* @brief Set credentials for given SSID.
|
||||
*
|
||||
* @param[in] ssid SSID to look for
|
||||
* @param[in] ssid_len length of SSID
|
||||
* @param[in] type Wi-Fi security type
|
||||
* @param[in] bssid BSSID (may be NULL)
|
||||
* @param[in] bssid_len length of BSSID buffer (either 0 or WIFI_MAC_ADDR_LEN)
|
||||
* @param[in] password password
|
||||
* @param[in] password_len length of password
|
||||
* @param[in] flags flags
|
||||
* @param[in] channel Channel
|
||||
* @param[in] timeout Timeout
|
||||
*
|
||||
* @return 0 Success. Credentials are stored in persistent storage.
|
||||
* @return -EINVAL A required buffer was NULL or security type is not supported.
|
||||
* @return -ENOTSUP Security type is not supported.
|
||||
* @return -ENOBUFS All slots are already taken.
|
||||
*/
|
||||
int wifi_credentials_set_personal(const char *ssid, size_t ssid_len, enum wifi_security_type type,
|
||||
const uint8_t *bssid, size_t bssid_len, const char *password,
|
||||
size_t password_len, uint32_t flags, uint8_t channel,
|
||||
uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Get credentials for given SSID by struct.
|
||||
*
|
||||
* @param[in] ssid SSID to look for
|
||||
* @param[in] ssid_len length of SSID
|
||||
* @param[out] buf credentials Pointer to struct where credentials are stored
|
||||
*
|
||||
* @return 0 Success.
|
||||
* @return -ENOENT No network with this SSID was found.
|
||||
* @return -EINVAL A required buffer was NULL or too small.
|
||||
* @return -EPROTO The network with this SSID is not a personal network.
|
||||
*/
|
||||
int wifi_credentials_get_by_ssid_personal_struct(const char *ssid, size_t ssid_len,
|
||||
struct wifi_credentials_personal *buf);
|
||||
|
||||
/**
|
||||
* @brief Set credentials for given SSID by struct.
|
||||
*
|
||||
* @param[in] creds credentials Pointer to struct from which credentials are loaded
|
||||
*
|
||||
* @return 0 Success.
|
||||
* @return -ENOENT No network with this SSID was found.
|
||||
* @return -EINVAL A required buffer was NULL or incorrect size.
|
||||
* @return -ENOBUFS All slots are already taken.
|
||||
*/
|
||||
int wifi_credentials_set_personal_struct(const struct wifi_credentials_personal *creds);
|
||||
|
||||
/**
|
||||
* @brief Delete credentials for given SSID.
|
||||
*
|
||||
* @param[in] ssid SSID to look for
|
||||
* @param[in] ssid_len length of SSID
|
||||
*
|
||||
* @return -ENOENT if No network with this SSID was found.
|
||||
* @return 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int wifi_credentials_delete_by_ssid(const char *ssid, size_t ssid_len);
|
||||
|
||||
/**
|
||||
* @brief Check if credentials storage is empty.
|
||||
*
|
||||
* @return true if credential storage is empty, otherwise false
|
||||
*/
|
||||
bool wifi_credentials_is_empty(void);
|
||||
|
||||
/**
|
||||
* @brief Deletes all stored Wi-Fi credentials.
|
||||
*
|
||||
* This function deletes all Wi-Fi credentials that have been stored in the system.
|
||||
* It is typically used when you want to clear all saved networks.
|
||||
*
|
||||
* @return 0 on successful, otherwise a negative error code
|
||||
*/
|
||||
int wifi_credentials_delete_all(void);
|
||||
|
||||
/**
|
||||
* @brief Callback type for wifi_credentials_for_each_ssid.
|
||||
* @param[in] cb_arg arguments for the callback function. Appropriate cb_arg is
|
||||
* transferred by wifi_credentials_for_each_ssid.
|
||||
* @param[in] ssid SSID
|
||||
* @param[in] ssid_len length of SSID
|
||||
*/
|
||||
typedef void (*wifi_credentials_ssid_cb)(void *cb_arg, const char *ssid, size_t ssid_len);
|
||||
|
||||
/**
|
||||
* @brief Call callback for each registered SSID.
|
||||
*
|
||||
* @param cb callback
|
||||
* @param cb_arg argument for callback function
|
||||
*/
|
||||
void wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb, void *cb_arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* WIFI_CREDENTIALS_H__ */
|
||||
|
|
@ -42,7 +42,13 @@ tests:
|
|||
integration_platforms:
|
||||
- frdm_k64f
|
||||
sample.net.wifi.nrf70dk:
|
||||
extra_args: CONFIG_NRF_WIFI_BUILD_ONLY_MODE=y
|
||||
extra_args:
|
||||
- CONFIG_NRF_WIFI_BUILD_ONLY_MODE=y
|
||||
- CONFIG_WIFI_CREDENTIALS=y
|
||||
- CONFIG_FLASH=y
|
||||
- CONFIG_FLASH_MAP=y
|
||||
- CONFIG_NVS=y
|
||||
- CONFIG_SETTINGS=y
|
||||
platform_allow:
|
||||
- nrf7002dk/nrf5340/cpuapp
|
||||
- nrf7002dk/nrf5340/cpuapp/nrf7001
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ add_subdirectory_ifdef(CONFIG_NET_SHELL shell)
|
|||
add_subdirectory_ifdef(CONFIG_NET_TRICKLE trickle)
|
||||
add_subdirectory_ifdef(CONFIG_NET_DHCPV6 dhcpv6)
|
||||
add_subdirectory_ifdef(CONFIG_PROMETHEUS prometheus)
|
||||
add_subdirectory_ifdef(CONFIG_WIFI_CREDENTIALS wifi_credentials)
|
||||
|
||||
if (CONFIG_NET_DHCPV4 OR CONFIG_NET_DHCPV4_SERVER)
|
||||
add_subdirectory(dhcpv4)
|
||||
|
|
|
|||
|
|
@ -51,4 +51,6 @@ source "subsys/net/lib/zperf/Kconfig"
|
|||
|
||||
source "subsys/net/lib/prometheus/Kconfig"
|
||||
|
||||
source "subsys/net/lib/wifi_credentials/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
36
subsys/net/lib/wifi_credentials/CMakeLists.txt
Normal file
36
subsys/net/lib/wifi_credentials/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#
|
||||
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
zephyr_library_named(wifi_credentials)
|
||||
zephyr_library_sources(wifi_credentials.c)
|
||||
|
||||
if (CONFIG_WIFI_CREDENTIALS_BACKEND_PSA)
|
||||
zephyr_library_include_directories(
|
||||
$<TARGET_PROPERTY:tfm,TFM_BINARY_DIR>/api_ns/interface/include
|
||||
)
|
||||
endif()
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_WIFI_CREDENTIALS_BACKEND_SETTINGS
|
||||
wifi_credentials_backend_settings.c)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_WIFI_CREDENTIALS_BACKEND_PSA
|
||||
wifi_credentials_backend_psa.c)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_WIFI_CREDENTIALS_BACKEND_NONE
|
||||
wifi_credentials_backend_none.c)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_WIFI_CREDENTIALS_SHELL
|
||||
wifi_credentials_shell.c)
|
||||
|
||||
if(WIFI_CREDENTIALS_STATIC_SSID)
|
||||
message(WARNING
|
||||
"Static Wi-Fi configuration is used, please remove before deployment!"
|
||||
)
|
||||
endif()
|
||||
109
subsys/net/lib/wifi_credentials/Kconfig
Normal file
109
subsys/net/lib/wifi_credentials/Kconfig
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#
|
||||
# Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig WIFI_CREDENTIALS
|
||||
bool "WIFI credentials management"
|
||||
select EXPERIMENTAL
|
||||
help
|
||||
Enable WiFi credentials management subsystem.
|
||||
|
||||
if WIFI_CREDENTIALS
|
||||
|
||||
module = WIFI_CREDENTIALS
|
||||
module-str = wifi_credentials
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
choice WIFI_CREDENTIALS_BACKEND
|
||||
prompt "WiFi credentials backend"
|
||||
default WIFI_CREDENTIALS_BACKEND_PSA if BUILD_WITH_TFM
|
||||
default WIFI_CREDENTIALS_BACKEND_SETTINGS
|
||||
default WIFI_CREDENTIALS_BACKEND_NONE if WIFI_CREDENTIALS_STATIC
|
||||
help
|
||||
Selects whether to use PSA Protected Storage or the Zephyr settings subsystem
|
||||
for credentials storage.
|
||||
|
||||
config WIFI_CREDENTIALS_BACKEND_SETTINGS
|
||||
bool "Zephyr Settings"
|
||||
depends on SETTINGS
|
||||
depends on !SETTINGS_NONE
|
||||
|
||||
config WIFI_CREDENTIALS_BACKEND_PSA
|
||||
bool "PSA Protected Storage"
|
||||
depends on BUILD_WITH_TFM
|
||||
|
||||
config WIFI_CREDENTIALS_BACKEND_NONE
|
||||
bool "No credentials storage"
|
||||
depends on WIFI_CREDENTIALS_STATIC
|
||||
|
||||
endchoice
|
||||
|
||||
config WIFI_CREDENTIALS_MAX_ENTRIES
|
||||
int "Number of supported WiFi credentials"
|
||||
default 2
|
||||
help
|
||||
This detemines how many different WiFi networks can be configured at a time.
|
||||
|
||||
config WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH
|
||||
int "Max. length of SAE password"
|
||||
default 128
|
||||
help
|
||||
There is no official limit on SAE password length,
|
||||
but for example Linux 6.0 has a hardcoded limit of 128 bytes.
|
||||
|
||||
config WIFI_CREDENTIALS_SHELL
|
||||
bool "Shell commands to manage Wi-Fi credentials"
|
||||
default y
|
||||
depends on SHELL
|
||||
depends on !WIFI_CREDENTIALS_BACKEND_NONE
|
||||
|
||||
endif # WIFI_CREDENTIALS
|
||||
|
||||
if WIFI_CREDENTIALS_BACKEND_PSA
|
||||
|
||||
config WIFI_CREDENTIALS_BACKEND_PSA_OFFSET
|
||||
int "PSA_KEY_ID range offset"
|
||||
default 0
|
||||
help
|
||||
The PSA specification mandates to set key identifiers for keys
|
||||
with persistent lifetime. The users of the PSA API are responsible (WIFI credentials
|
||||
management is user of PSA API) to provide correct and unique identifiers.
|
||||
|
||||
endif # WIFI_CREDENTIALS_BACKEND_PSA
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC
|
||||
bool "Static Wi-Fi network configuration"
|
||||
|
||||
if WIFI_CREDENTIALS_STATIC
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_SSID
|
||||
string "SSID of statically configured WiFi network"
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_PASSWORD
|
||||
string "Password of statically configured Wi-Fi network"
|
||||
default ""
|
||||
|
||||
choice WIFI_CREDENTIALS_STATIC_TYPE
|
||||
prompt "Static Wi-Fi network security type"
|
||||
default WIFI_CREDENTIALS_STATIC_TYPE_PSK
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_TYPE_OPEN
|
||||
bool "OPEN"
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_TYPE_PSK
|
||||
bool "WPA2-PSK"
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_TYPE_PSK_SHA256
|
||||
bool "WPA2-PSK-SHA256"
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_TYPE_SAE
|
||||
bool "SAE"
|
||||
|
||||
config WIFI_CREDENTIALS_STATIC_TYPE_WPA_PSK
|
||||
bool "WPA-PSK"
|
||||
|
||||
endchoice
|
||||
|
||||
endif
|
||||
409
subsys/net/lib/wifi_credentials/wifi_credentials.c
Normal file
409
subsys/net/lib/wifi_credentials/wifi_credentials.c
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/net/wifi_credentials.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "wifi_credentials_internal.h"
|
||||
|
||||
LOG_MODULE_REGISTER(wifi_credentials, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
|
||||
|
||||
static K_MUTEX_DEFINE(wifi_credentials_mutex);
|
||||
|
||||
/* SSID cache: maps SSIDs to their storage indices */
|
||||
static char ssid_cache[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES][WIFI_SSID_MAX_LEN];
|
||||
static size_t ssid_cache_lengths[CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES];
|
||||
|
||||
/**
|
||||
* @brief Finds index of given SSID if it exists.
|
||||
*
|
||||
* @param ssid SSID to look for (buffer of WIFI_SSID_MAX_LEN length)
|
||||
* @return index if entry is found, -1 otherwise
|
||||
*/
|
||||
static inline ssize_t lookup_idx(const uint8_t *ssid, size_t ssid_len)
|
||||
{
|
||||
for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
|
||||
if (ssid_len != ssid_cache_lengths[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(ssid, ssid_cache[i], ssid_len) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine whether an index is currently used for storing network credentials.
|
||||
*
|
||||
* @param idx credential index
|
||||
* @return true if index is used, false otherwise
|
||||
*/
|
||||
static inline bool is_entry_used(size_t idx)
|
||||
{
|
||||
return ssid_cache_lengths[idx] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds unused index to store new entry at.
|
||||
*
|
||||
* @return index if empty slot is found, -1 otherwise
|
||||
*/
|
||||
static inline ssize_t lookup_unused_idx(void)
|
||||
{
|
||||
for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
|
||||
if (!is_entry_used(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int init(void)
|
||||
{
|
||||
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
|
||||
ret = wifi_credentials_backend_init();
|
||||
if (ret) {
|
||||
LOG_ERR("Initializing WiFi credentials storage backend failed, err: %d", ret);
|
||||
}
|
||||
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wifi_credentials_cache_ssid(size_t idx, const struct wifi_credentials_header *buf)
|
||||
{
|
||||
memcpy(ssid_cache[idx], buf->ssid, buf->ssid_len);
|
||||
ssid_cache_lengths[idx] = buf->ssid_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear entry in SSID cache.
|
||||
*
|
||||
* @param idx credential index
|
||||
*/
|
||||
void wifi_credentials_uncache_ssid(size_t idx)
|
||||
{
|
||||
ssid_cache_lengths[idx] = 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_get_by_ssid_personal_struct(const char *ssid, size_t ssid_len,
|
||||
struct wifi_credentials_personal *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
|
||||
LOG_ERR("Cannot retrieve WiFi credentials, SSID has invalid format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (buf == NULL) {
|
||||
LOG_ERR("Cannot retrieve WiFi credentials, "
|
||||
"destination struct pointer cannot be NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
|
||||
ssize_t idx = lookup_idx(ssid, ssid_len);
|
||||
|
||||
if (idx == -1) {
|
||||
LOG_DBG("Cannot retrieve WiFi credentials, no entry found for the provided SSID");
|
||||
ret = -ENOENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = wifi_credentials_load_entry(idx, buf, sizeof(struct wifi_credentials_personal));
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to load WiFi credentials at index %d, err: %d", idx, ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (buf->header.type != WIFI_SECURITY_TYPE_NONE &&
|
||||
buf->header.type != WIFI_SECURITY_TYPE_PSK &&
|
||||
buf->header.type != WIFI_SECURITY_TYPE_PSK_SHA256 &&
|
||||
buf->header.type != WIFI_SECURITY_TYPE_SAE &&
|
||||
buf->header.type != WIFI_SECURITY_TYPE_WPA_PSK) {
|
||||
LOG_ERR("Requested WiFi credentials entry is corrupted");
|
||||
ret = -EPROTO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wifi_credentials_set_personal_struct(const struct wifi_credentials_personal *creds)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (creds->header.ssid_len > WIFI_SSID_MAX_LEN || creds->header.ssid_len == 0) {
|
||||
LOG_ERR("Cannot set WiFi credentials, SSID has invalid format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (creds == NULL) {
|
||||
LOG_ERR("Cannot set WiFi credentials, provided struct pointer cannot be NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
|
||||
ssize_t idx = lookup_idx(creds->header.ssid, creds->header.ssid_len);
|
||||
|
||||
if (idx == -1) {
|
||||
idx = lookup_unused_idx();
|
||||
if (idx == -1) {
|
||||
LOG_ERR("Cannot store WiFi credentials, no space left");
|
||||
ret = -ENOBUFS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
ret = wifi_credentials_store_entry(idx, creds, sizeof(struct wifi_credentials_personal));
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to store WiFi credentials at index %d, err: %d", idx, ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wifi_credentials_cache_ssid(idx, &creds->header);
|
||||
|
||||
exit:
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wifi_credentials_set_personal(const char *ssid, size_t ssid_len, enum wifi_security_type type,
|
||||
const uint8_t *bssid, size_t bssid_len, const char *password,
|
||||
size_t password_len, uint32_t flags, uint8_t channel,
|
||||
uint32_t timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t buf[ENTRY_MAX_LEN] = {0};
|
||||
|
||||
if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
|
||||
LOG_ERR("Cannot set WiFi credentials, SSID has invalid format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (flags & WIFI_CREDENTIALS_FLAG_BSSID &&
|
||||
(bssid_len != WIFI_MAC_ADDR_LEN || bssid == NULL)) {
|
||||
LOG_ERR("Cannot set WiFi credentials, "
|
||||
"provided flags indicated BSSID, but no BSSID provided");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((type != WIFI_SECURITY_TYPE_NONE && (password_len == 0 || password == NULL)) ||
|
||||
(password_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN)) {
|
||||
LOG_ERR("Cannot set WiFi credentials, password not provided or invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* pack entry */
|
||||
struct wifi_credentials_header *header = (struct wifi_credentials_header *)buf;
|
||||
|
||||
header->type = type;
|
||||
memcpy(header->ssid, ssid, ssid_len);
|
||||
header->ssid_len = ssid_len;
|
||||
header->flags = flags;
|
||||
header->channel = channel;
|
||||
header->timeout = timeout;
|
||||
|
||||
if (flags & WIFI_CREDENTIALS_FLAG_BSSID) {
|
||||
memcpy(header->bssid, bssid, WIFI_MAC_ADDR_LEN);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case WIFI_SECURITY_TYPE_NONE:
|
||||
break;
|
||||
case WIFI_SECURITY_TYPE_PSK:
|
||||
case WIFI_SECURITY_TYPE_PSK_SHA256:
|
||||
case WIFI_SECURITY_TYPE_WPA_PSK:
|
||||
case WIFI_SECURITY_TYPE_SAE: {
|
||||
struct wifi_credentials_personal *header_personal =
|
||||
(struct wifi_credentials_personal *)buf;
|
||||
|
||||
memcpy(header_personal->password, password, password_len);
|
||||
header_personal->password_len = password_len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERR("Cannot set WiFi credentials, "
|
||||
"provided security type %d is unsupported",
|
||||
type);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* store entry */
|
||||
ret = wifi_credentials_set_personal_struct((struct wifi_credentials_personal *)buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wifi_credentials_get_by_ssid_personal(const char *ssid, size_t ssid_len,
|
||||
enum wifi_security_type *type, uint8_t *bssid_buf,
|
||||
size_t bssid_buf_len, char *password_buf,
|
||||
size_t password_buf_len, size_t *password_len,
|
||||
uint32_t *flags, uint8_t *channel, uint32_t *timeout)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t buf[ENTRY_MAX_LEN] = {0};
|
||||
|
||||
if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
|
||||
LOG_ERR("Cannot retrieve WiFi credentials, SSID has invalid format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bssid_buf_len != WIFI_MAC_ADDR_LEN || bssid_buf == NULL) {
|
||||
LOG_ERR("BSSID buffer needs to be provided");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (password_buf == NULL || password_buf_len > WIFI_CREDENTIALS_MAX_PASSWORD_LEN ||
|
||||
password_buf_len == 0) {
|
||||
LOG_ERR("WiFi password buffer needs to be provided");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* load entry */
|
||||
ret = wifi_credentials_get_by_ssid_personal_struct(ssid, ssid_len,
|
||||
(struct wifi_credentials_personal *)buf);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* unpack entry*/
|
||||
struct wifi_credentials_header *header = (struct wifi_credentials_header *)buf;
|
||||
|
||||
*type = header->type;
|
||||
*flags = header->flags;
|
||||
*channel = header->channel;
|
||||
*timeout = header->timeout;
|
||||
|
||||
if (header->flags & WIFI_CREDENTIALS_FLAG_BSSID) {
|
||||
memcpy(bssid_buf, header->bssid, WIFI_MAC_ADDR_LEN);
|
||||
}
|
||||
|
||||
switch (header->type) {
|
||||
case WIFI_SECURITY_TYPE_NONE:
|
||||
break;
|
||||
case WIFI_SECURITY_TYPE_PSK:
|
||||
case WIFI_SECURITY_TYPE_PSK_SHA256:
|
||||
case WIFI_SECURITY_TYPE_WPA_PSK:
|
||||
case WIFI_SECURITY_TYPE_SAE: {
|
||||
struct wifi_credentials_personal *header_personal =
|
||||
(struct wifi_credentials_personal *)buf;
|
||||
|
||||
memcpy(password_buf, header_personal->password, header_personal->password_len);
|
||||
*password_len = header_personal->password_len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERR("Cannot get WiFi credentials, "
|
||||
"the requested credentials have invalid WIFI_SECURITY_TYPE");
|
||||
ret = -EPROTO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wifi_credentials_delete_by_ssid(const char *ssid, size_t ssid_len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ssid == NULL || ssid_len > WIFI_SSID_MAX_LEN || ssid_len == 0) {
|
||||
LOG_ERR("Cannot delete WiFi credentials, SSID has invalid format");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
ssize_t idx = lookup_idx(ssid, ssid_len);
|
||||
|
||||
if (idx == -1) {
|
||||
LOG_DBG("WiFi credentials entry was not found");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = wifi_credentials_delete_entry(idx);
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to delete WiFi credentials index %d, err: %d", idx, ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
wifi_credentials_uncache_ssid(idx);
|
||||
|
||||
exit:
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wifi_credentials_for_each_ssid(wifi_credentials_ssid_cb cb, void *cb_arg)
|
||||
{
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
|
||||
for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
|
||||
if (is_entry_used(i)) {
|
||||
cb(cb_arg, ssid_cache[i], ssid_cache_lengths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
}
|
||||
|
||||
bool wifi_credentials_is_empty(void)
|
||||
{
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
|
||||
for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
|
||||
if (is_entry_used(i)) {
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
int wifi_credentials_delete_all(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
k_mutex_lock(&wifi_credentials_mutex, K_FOREVER);
|
||||
for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
|
||||
if (is_entry_used(i)) {
|
||||
ret = wifi_credentials_delete_entry(i);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to delete WiFi credentials index %d, err: %d", i,
|
||||
ret);
|
||||
break;
|
||||
}
|
||||
|
||||
wifi_credentials_uncache_ssid(i);
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_unlock(&wifi_credentials_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYS_INIT(init, POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "wifi_credentials_internal.h"
|
||||
|
||||
int wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len)
|
||||
{
|
||||
ARG_UNUSED(idx);
|
||||
ARG_UNUSED(buf);
|
||||
ARG_UNUSED(buf_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_delete_entry(size_t idx)
|
||||
{
|
||||
ARG_UNUSED(idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len)
|
||||
{
|
||||
ARG_UNUSED(idx);
|
||||
ARG_UNUSED(buf);
|
||||
ARG_UNUSED(buf_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_backend_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include "psa/crypto.h"
|
||||
|
||||
#include "wifi_credentials_internal.h"
|
||||
|
||||
LOG_MODULE_REGISTER(wifi_credentials_backend, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
|
||||
|
||||
#define WIFI_CREDENTIALS_BACKEND_PSA_KEY_ID_USER_MIN \
|
||||
(PSA_KEY_ID_USER_MIN + CONFIG_WIFI_CREDENTIALS_BACKEND_PSA_OFFSET)
|
||||
|
||||
BUILD_ASSERT((WIFI_CREDENTIALS_BACKEND_PSA_KEY_ID_USER_MIN + CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES) <=
|
||||
PSA_KEY_ID_USER_MAX,
|
||||
"WIFI credentials management PSA key id range exceeds PSA_KEY_ID_USER_MAX.");
|
||||
|
||||
int wifi_credentials_backend_init(void)
|
||||
{
|
||||
psa_status_t ret;
|
||||
uint8_t buf[ENTRY_MAX_LEN];
|
||||
|
||||
for (size_t i = 0; i < CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES; ++i) {
|
||||
size_t length_read = 0;
|
||||
size_t key_id = i + WIFI_CREDENTIALS_BACKEND_PSA_KEY_ID_USER_MIN;
|
||||
|
||||
ret = psa_export_key(key_id, buf, ARRAY_SIZE(buf), &length_read);
|
||||
if (ret == PSA_SUCCESS && length_read == ENTRY_MAX_LEN) {
|
||||
wifi_credentials_cache_ssid(i, (struct wifi_credentials_header *)buf);
|
||||
} else if (ret != PSA_ERROR_INVALID_HANDLE) {
|
||||
LOG_ERR("psa_export_key failed, err: %d", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len)
|
||||
{
|
||||
psa_status_t ret;
|
||||
psa_key_attributes_t key_attributes = {0};
|
||||
psa_key_id_t key_id;
|
||||
|
||||
psa_set_key_id(&key_attributes, idx + WIFI_CREDENTIALS_BACKEND_PSA_KEY_ID_USER_MIN);
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_EXPORT);
|
||||
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_PERSISTENT);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_NONE);
|
||||
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_RAW_DATA);
|
||||
psa_set_key_bits(&key_attributes, buf_len * 8);
|
||||
|
||||
ret = psa_import_key(&key_attributes, buf, buf_len, &key_id);
|
||||
if (ret == PSA_ERROR_ALREADY_EXISTS) {
|
||||
LOG_ERR("psa_import_key failed, duplicate key: %d", ret);
|
||||
return -EEXIST;
|
||||
} else if (ret != PSA_SUCCESS) {
|
||||
LOG_ERR("psa_import_key failed, err: %d", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_delete_entry(size_t idx)
|
||||
{
|
||||
psa_status_t ret = psa_destroy_key(idx + WIFI_CREDENTIALS_BACKEND_PSA_KEY_ID_USER_MIN);
|
||||
|
||||
if (ret != PSA_SUCCESS) {
|
||||
LOG_ERR("psa_destroy_key failed, err: %d", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len)
|
||||
{
|
||||
size_t length_read = 0;
|
||||
size_t key_id = idx + WIFI_CREDENTIALS_BACKEND_PSA_KEY_ID_USER_MIN;
|
||||
psa_status_t ret;
|
||||
|
||||
ret = psa_export_key(key_id, buf, buf_len, &length_read);
|
||||
if (ret != PSA_SUCCESS) {
|
||||
LOG_ERR("psa_export_key failed, err: %d", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (buf_len != length_read) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <stdlib.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
|
||||
#include "wifi_credentials_internal.h"
|
||||
|
||||
LOG_MODULE_REGISTER(wifi_credentials_backend, CONFIG_WIFI_CREDENTIALS_LOG_LEVEL);
|
||||
|
||||
BUILD_ASSERT(ENTRY_MAX_LEN <= SETTINGS_MAX_VAL_LEN);
|
||||
|
||||
#define WIFI_CREDENTIALS_SBE_BASE_KEY "wifi_cred"
|
||||
#define WIFI_CREDENTIALS_SBE_KEY_SIZE \
|
||||
sizeof(WIFI_CREDENTIALS_SBE_BASE_KEY "/" STRINGIFY(CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES))
|
||||
#define WIFI_CREDENTIALS_SBE_KEY_FMT WIFI_CREDENTIALS_SBE_BASE_KEY "/%d"
|
||||
|
||||
/* Type of the callback argument used in the function below. */
|
||||
struct zephyr_settings_backend_load_cb_arg {
|
||||
uint8_t *buf;
|
||||
size_t buf_len;
|
||||
size_t idx;
|
||||
bool found;
|
||||
};
|
||||
|
||||
/* This callback function is used to retrieve credentials on demand. */
|
||||
static int zephyr_settings_backend_load_val_cb(const char *key, size_t len,
|
||||
settings_read_cb read_cb, void *cb_arg, void *param)
|
||||
{
|
||||
struct zephyr_settings_backend_load_cb_arg *arg = param;
|
||||
int idx = atoi(key);
|
||||
|
||||
if (arg->idx != idx) {
|
||||
LOG_DBG("Skipping index [%s]", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len != arg->buf_len) {
|
||||
LOG_ERR("Settings error: invalid settings length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size_t length_read = read_cb(cb_arg, arg->buf, arg->buf_len);
|
||||
|
||||
/* value validation */
|
||||
if (length_read < len) {
|
||||
LOG_ERR("Settings error: entry incomplete");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
arg->found = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This callback function is used to initialize the SSID cache. */
|
||||
static int zephyr_settings_backend_load_key_cb(const char *key, size_t len,
|
||||
settings_read_cb read_cb, void *cb_arg, void *param)
|
||||
{
|
||||
ARG_UNUSED(param);
|
||||
|
||||
/* key validation */
|
||||
if (!key) {
|
||||
LOG_ERR("Settings error: no key");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int idx = atoi(key);
|
||||
|
||||
if ((idx == 0 && strcmp(key, "0") != 0) || idx >= CONFIG_WIFI_CREDENTIALS_MAX_ENTRIES) {
|
||||
LOG_ERR("Settings error: index too large");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (len < sizeof(struct wifi_credentials_header)) {
|
||||
LOG_ERR("Settings error: invalid settings length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint8_t buf[ENTRY_MAX_LEN];
|
||||
size_t length_read = read_cb(cb_arg, buf, ARRAY_SIZE(buf));
|
||||
|
||||
/* value validation */
|
||||
if (length_read < len) {
|
||||
LOG_ERR("Settings error: entry incomplete");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
wifi_credentials_cache_ssid(idx, (struct wifi_credentials_header *)buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_credentials_backend_init(void)
|
||||
{
|
||||
int ret = settings_subsys_init();
|
||||
|
||||
if (ret) {
|
||||
LOG_ERR("Initializing settings subsystem failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = settings_load_subtree_direct(WIFI_CREDENTIALS_SBE_BASE_KEY,
|
||||
zephyr_settings_backend_load_key_cb, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len)
|
||||
{
|
||||
char settings_name_buf[WIFI_CREDENTIALS_SBE_KEY_SIZE] = {0};
|
||||
|
||||
int ret = snprintk(settings_name_buf, ARRAY_SIZE(settings_name_buf),
|
||||
WIFI_CREDENTIALS_SBE_KEY_FMT, idx);
|
||||
|
||||
if (ret < 0 || ret == ARRAY_SIZE(settings_name_buf)) {
|
||||
LOG_ERR("WiFi credentials settings key could not be generated, idx: %d", idx);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return settings_save_one(settings_name_buf, buf, buf_len);
|
||||
}
|
||||
|
||||
int wifi_credentials_delete_entry(size_t idx)
|
||||
{
|
||||
char settings_name_buf[WIFI_CREDENTIALS_SBE_KEY_SIZE] = {0};
|
||||
|
||||
int ret = snprintk(settings_name_buf, ARRAY_SIZE(settings_name_buf),
|
||||
WIFI_CREDENTIALS_SBE_KEY_FMT, idx);
|
||||
|
||||
if (ret < 0 || ret == ARRAY_SIZE(settings_name_buf)) {
|
||||
LOG_ERR("WiFi credentials settings key could not be generated, idx: %d", idx);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return settings_delete(settings_name_buf);
|
||||
}
|
||||
|
||||
int wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len)
|
||||
{
|
||||
struct zephyr_settings_backend_load_cb_arg arg = {
|
||||
.buf = buf,
|
||||
.buf_len = buf_len,
|
||||
.idx = idx,
|
||||
.found = false,
|
||||
};
|
||||
int ret;
|
||||
|
||||
/* Browse through the settings entries with custom callback to load the whole entry. */
|
||||
ret = settings_load_subtree_direct(WIFI_CREDENTIALS_SBE_BASE_KEY,
|
||||
zephyr_settings_backend_load_val_cb, &arg);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!arg.found) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
60
subsys/net/lib/wifi_credentials/wifi_credentials_internal.h
Normal file
60
subsys/net/lib/wifi_credentials/wifi_credentials_internal.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/net/wifi_credentials.h>
|
||||
|
||||
#define ENTRY_MAX_LEN sizeof(struct wifi_credentials_personal)
|
||||
|
||||
/**
|
||||
* @brief Write entry to SSID cache.
|
||||
*
|
||||
* @param idx credential index
|
||||
* @param buf encoded settings entry
|
||||
* @param buf_len length of buf
|
||||
*/
|
||||
void wifi_credentials_cache_ssid(size_t idx, const struct wifi_credentials_header *buf);
|
||||
|
||||
/**
|
||||
* @brief Clear entry in SSID cache.
|
||||
*
|
||||
* @param idx credential index
|
||||
*/
|
||||
void wifi_credentials_uncache_ssid(size_t idx);
|
||||
|
||||
/**
|
||||
* @brief Stores settings entry in flash.
|
||||
*
|
||||
* @param idx credential index
|
||||
* @param buf encoded settings entry
|
||||
* @param buf_len length of buf
|
||||
* @return 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int wifi_credentials_store_entry(size_t idx, const void *buf, size_t buf_len);
|
||||
|
||||
/**
|
||||
* @brief Deletes settings entry from flash.
|
||||
*
|
||||
* @param idx credential index
|
||||
* @return 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int wifi_credentials_delete_entry(size_t idx);
|
||||
|
||||
/**
|
||||
* @brief Loads settings entry from flash.
|
||||
*
|
||||
* @param idx credential index
|
||||
* @param buf encoded settings entry
|
||||
* @param buf_len length of buf
|
||||
* @return 0 on success, otherwise a negative error code
|
||||
*/
|
||||
int wifi_credentials_load_entry(size_t idx, void *buf, size_t buf_len);
|
||||
|
||||
/**
|
||||
* @brief Initialize backend.
|
||||
* @note Is called by the library on system startup.
|
||||
*
|
||||
*/
|
||||
int wifi_credentials_backend_init(void);
|
||||
313
subsys/net/lib/wifi_credentials/wifi_credentials_shell.c
Normal file
313
subsys/net/lib/wifi_credentials/wifi_credentials_shell.c
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200809L /* For strnlen() */
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <zephyr/init.h>
|
||||
|
||||
#include <zephyr/net/net_if.h>
|
||||
#include <zephyr/net/wifi_mgmt.h>
|
||||
#include <zephyr/net/net_event.h>
|
||||
#include <zephyr/net/net_l2.h>
|
||||
#include <zephyr/net/ethernet.h>
|
||||
|
||||
#include <zephyr/net/wifi_credentials.h>
|
||||
|
||||
#define MACSTR "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
|
||||
|
||||
static void print_network_info(void *cb_arg, const char *ssid, size_t ssid_len)
|
||||
{
|
||||
int ret = 0;
|
||||
struct wifi_credentials_personal creds = {0};
|
||||
const struct shell *sh = (const struct shell *)cb_arg;
|
||||
|
||||
ret = wifi_credentials_get_by_ssid_personal_struct(ssid, ssid_len, &creds);
|
||||
if (ret) {
|
||||
shell_error(sh,
|
||||
"An error occurred when trying to load credentials for network \"%.*s\""
|
||||
". err: %d",
|
||||
ssid_len, ssid, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT,
|
||||
" network ssid: \"%.*s\", ssid_len: %d, type: %s", ssid_len, ssid, ssid_len,
|
||||
wifi_security_txt(creds.header.type));
|
||||
|
||||
if (creds.header.type == WIFI_SECURITY_TYPE_PSK ||
|
||||
creds.header.type == WIFI_SECURITY_TYPE_PSK_SHA256 ||
|
||||
creds.header.type == WIFI_SECURITY_TYPE_SAE ||
|
||||
creds.header.type == WIFI_SECURITY_TYPE_WPA_PSK) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT,
|
||||
", password: \"%.*s\", password_len: %d", creds.password_len,
|
||||
creds.password, creds.password_len);
|
||||
}
|
||||
|
||||
if (creds.header.flags & WIFI_CREDENTIALS_FLAG_BSSID) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", bssid: " MACSTR,
|
||||
creds.header.bssid[0], creds.header.bssid[1], creds.header.bssid[2],
|
||||
creds.header.bssid[3], creds.header.bssid[4], creds.header.bssid[5]);
|
||||
}
|
||||
|
||||
if (creds.header.flags & WIFI_CREDENTIALS_FLAG_2_4GHz) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", band: 2.4GHz");
|
||||
}
|
||||
|
||||
if (creds.header.flags & WIFI_CREDENTIALS_FLAG_5GHz) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", band: 5GHz");
|
||||
}
|
||||
|
||||
if (creds.header.channel) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", channel: %d",
|
||||
creds.header.channel);
|
||||
}
|
||||
|
||||
if (creds.header.flags & WIFI_CREDENTIALS_FLAG_FAVORITE) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", favorite");
|
||||
}
|
||||
|
||||
if (creds.header.flags & WIFI_CREDENTIALS_FLAG_MFP_REQUIRED) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", MFP_REQUIRED");
|
||||
} else if (creds.header.flags & WIFI_CREDENTIALS_FLAG_MFP_DISABLED) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", MFP_DISABLED");
|
||||
} else {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", MFP_OPTIONAL");
|
||||
}
|
||||
|
||||
if (creds.header.timeout) {
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, ", timeout: %d",
|
||||
creds.header.timeout);
|
||||
}
|
||||
|
||||
shell_fprintf(sh, SHELL_VT100_COLOR_DEFAULT, "\n");
|
||||
}
|
||||
|
||||
static enum wifi_security_type parse_sec_type(const char *s)
|
||||
{
|
||||
if (strcmp("OPEN", s) == 0) {
|
||||
return WIFI_SECURITY_TYPE_NONE;
|
||||
}
|
||||
|
||||
if (strcmp("WPA2-PSK", s) == 0) {
|
||||
return WIFI_SECURITY_TYPE_PSK;
|
||||
}
|
||||
|
||||
if (strcmp("WPA2-PSK-SHA256", s) == 0) {
|
||||
return WIFI_SECURITY_TYPE_PSK_SHA256;
|
||||
}
|
||||
|
||||
if (strcmp("WPA3-SAE", s) == 0) {
|
||||
return WIFI_SECURITY_TYPE_SAE;
|
||||
}
|
||||
|
||||
if (strcmp("WPA-PSK", s) == 0) {
|
||||
return WIFI_SECURITY_TYPE_WPA_PSK;
|
||||
}
|
||||
|
||||
return WIFI_SECURITY_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
static enum wifi_frequency_bands parse_band(const char *s)
|
||||
{
|
||||
if (strcmp("2.4GHz", s) == 0) {
|
||||
return WIFI_FREQ_BAND_2_4_GHZ;
|
||||
}
|
||||
|
||||
if (strcmp("5GHz", s) == 0) {
|
||||
return WIFI_FREQ_BAND_5_GHZ;
|
||||
}
|
||||
|
||||
if (strcmp("6GHz", s) == 0) {
|
||||
return WIFI_FREQ_BAND_6_GHZ;
|
||||
}
|
||||
|
||||
return WIFI_FREQ_BAND_UNKNOWN;
|
||||
}
|
||||
|
||||
static int cmd_add_network(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (argc < 3) {
|
||||
goto help;
|
||||
}
|
||||
|
||||
if (strnlen(argv[1], WIFI_SSID_MAX_LEN + 1) > WIFI_SSID_MAX_LEN) {
|
||||
shell_error(sh, "SSID too long");
|
||||
goto help;
|
||||
}
|
||||
|
||||
struct wifi_credentials_personal creds = {
|
||||
.header.ssid_len = strlen(argv[1]),
|
||||
.header.type = parse_sec_type(argv[2]),
|
||||
};
|
||||
|
||||
memcpy(creds.header.ssid, argv[1], creds.header.ssid_len);
|
||||
|
||||
if (creds.header.type == WIFI_SECURITY_TYPE_UNKNOWN) {
|
||||
shell_error(sh, "Cannot parse security type");
|
||||
goto help;
|
||||
}
|
||||
|
||||
size_t arg_idx = 3;
|
||||
|
||||
if (creds.header.type == WIFI_SECURITY_TYPE_PSK ||
|
||||
creds.header.type == WIFI_SECURITY_TYPE_PSK_SHA256 ||
|
||||
creds.header.type == WIFI_SECURITY_TYPE_SAE ||
|
||||
creds.header.type == WIFI_SECURITY_TYPE_WPA_PSK) {
|
||||
/* parse passphrase */
|
||||
if (argc < 4) {
|
||||
shell_error(sh, "Missing password");
|
||||
goto help;
|
||||
}
|
||||
creds.password_len = strlen(argv[3]);
|
||||
if (creds.password_len < WIFI_PSK_MIN_LEN) {
|
||||
shell_error(sh, "Passphrase should be minimum %d characters",
|
||||
WIFI_PSK_MIN_LEN);
|
||||
goto help;
|
||||
}
|
||||
if ((creds.password_len > CONFIG_WIFI_CREDENTIALS_SAE_PASSWORD_LENGTH &&
|
||||
creds.header.type == WIFI_SECURITY_TYPE_SAE) ||
|
||||
(creds.password_len > WIFI_PSK_MAX_LEN &&
|
||||
creds.header.type != WIFI_SECURITY_TYPE_SAE)) {
|
||||
shell_error(sh, "Password is too long for this security type");
|
||||
goto help;
|
||||
}
|
||||
memcpy(creds.password, argv[3], creds.password_len);
|
||||
++arg_idx;
|
||||
}
|
||||
|
||||
if (arg_idx < argc) {
|
||||
/* look for bssid */
|
||||
ret = sscanf(argv[arg_idx], MACSTR, &creds.header.bssid[0], &creds.header.bssid[1],
|
||||
&creds.header.bssid[2], &creds.header.bssid[3], &creds.header.bssid[4],
|
||||
&creds.header.bssid[5]);
|
||||
if (ret == 6) {
|
||||
creds.header.flags |= WIFI_CREDENTIALS_FLAG_BSSID;
|
||||
++arg_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_idx < argc) {
|
||||
/* look for band */
|
||||
enum wifi_frequency_bands band = parse_band(argv[arg_idx]);
|
||||
|
||||
if (band == WIFI_FREQ_BAND_2_4_GHZ) {
|
||||
creds.header.flags |= WIFI_CREDENTIALS_FLAG_2_4GHz;
|
||||
++arg_idx;
|
||||
}
|
||||
if (band == WIFI_FREQ_BAND_5_GHZ) {
|
||||
creds.header.flags |= WIFI_CREDENTIALS_FLAG_5GHz;
|
||||
++arg_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_idx < argc) {
|
||||
/* look for channel */
|
||||
char *end;
|
||||
|
||||
creds.header.channel = strtol(argv[arg_idx], &end, 10);
|
||||
if (*end == '\0') {
|
||||
++arg_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_idx < argc) {
|
||||
/* look for favorite flag */
|
||||
if (strncmp("favorite", argv[arg_idx], strlen("favorite")) == 0) {
|
||||
creds.header.flags |= WIFI_CREDENTIALS_FLAG_FAVORITE;
|
||||
++arg_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_idx < argc) {
|
||||
/* look for mfp_disabled flag */
|
||||
if (strncmp("mfp_disabled", argv[arg_idx], strlen("mfp_disabled")) == 0) {
|
||||
creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_DISABLED;
|
||||
++arg_idx;
|
||||
} else if (strncmp("mfp_required", argv[arg_idx], strlen("mfp_required")) == 0) {
|
||||
creds.header.flags |= WIFI_CREDENTIALS_FLAG_MFP_REQUIRED;
|
||||
++arg_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_idx < argc) {
|
||||
/* look for timeout */
|
||||
char *end;
|
||||
|
||||
creds.header.timeout = strtol(argv[arg_idx], &end, 10);
|
||||
if (*end == '\0') {
|
||||
++arg_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_idx != argc) {
|
||||
for (size_t i = arg_idx; i < argc; ++i) {
|
||||
shell_warn(sh, "Unparsed arg: [%s]", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return wifi_credentials_set_personal_struct(&creds);
|
||||
help:
|
||||
shell_print(sh, "Usage: wifi_cred add \"network name\""
|
||||
" {OPEN, WPA2-PSK, WPA2-PSK-SHA256, WPA3-SAE, WPA-PSK}"
|
||||
" [psk/password]"
|
||||
" [bssid]"
|
||||
" [{2.4GHz, 5GHz}]"
|
||||
" [channel]"
|
||||
" [favorite]"
|
||||
" [mfp_disabled|mfp_required]"
|
||||
" [timeout]");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cmd_delete_network(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
shell_print(sh, "Usage: wifi_cred delete \"network name\"");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strnlen(argv[1], WIFI_SSID_MAX_LEN + 1) > WIFI_SSID_MAX_LEN) {
|
||||
shell_error(sh, "SSID too long");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
shell_print(sh, "\tDeleting network ssid: \"%s\", ssid_len: %d", argv[1],
|
||||
strlen(argv[1]));
|
||||
return wifi_credentials_delete_by_ssid(argv[1], strlen(argv[1]));
|
||||
}
|
||||
|
||||
static int cmd_list_networks(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
wifi_credentials_for_each_ssid(print_network_info, (void *)sh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_wifi_cred,
|
||||
SHELL_CMD_ARG(add, NULL,
|
||||
"Add network to storage.\n",
|
||||
cmd_add_network, 0, 0),
|
||||
SHELL_CMD_ARG(delete, NULL,
|
||||
"Delete network from storage.\n",
|
||||
cmd_delete_network,
|
||||
0, 0),
|
||||
SHELL_CMD_ARG(list, NULL,
|
||||
"List stored networks.\n",
|
||||
cmd_list_networks,
|
||||
0, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_SUBCMD_ADD((wifi), cred, &sub_wifi_cred,
|
||||
"Wifi credentials management.\n",
|
||||
NULL,
|
||||
0, 0);
|
||||
Loading…
Reference in a new issue