mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(nan): persist NIK/NPK credentials in NVS
Replace nik/nik_valid in wifi_nan_sync_config_t with reset_current_nvs_creds and use_nvs_for_caching. On NAN start, load the saved own NIK and peer credentials from NVS (or erase them when reset is requested); generate and persist a fresh own NIK only when none is valid and caching is enabled. PASN reuses the SAE module (PWE/crypto and the comeback-token mechanism), so define CONFIG_SAE whenever SoftAP-SAE or PASN is enabled. This fixes the undefined references to check_comeback_token()/auth_build_token_req() when SOFTAP config is disabled.
This commit is contained in:
@@ -604,9 +604,8 @@ typedef struct {
|
||||
uint8_t scan_time; /**< Scan time in seconds while searching for a NAN cluster */
|
||||
uint16_t warm_up_sec; /**< Warm up time before assuming NAN Anchor Master role */
|
||||
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
||||
uint8_t nik[ESP_WIFI_NAN_NIK_LEN]; /**< Optional NIK. Auto-generated when nik_valid is false. */
|
||||
uint8_t nik_valid: 1; /**< NIK present in nik[] and should be used as-is. */
|
||||
uint8_t reserved: 7; /**< Reserved for future use. */
|
||||
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
||||
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
||||
} wifi_nan_sync_config_t;
|
||||
|
||||
/**
|
||||
@@ -874,7 +873,9 @@ typedef struct {
|
||||
#define ESP_WIFI_NDP_ROLE_RESPONDER 2 /**< Responder role for NAN Data Path */
|
||||
|
||||
#define ESP_WIFI_NAN_NDP_PMK_LEN 32 /**< Length of NAN Datapath PMK */
|
||||
#define ESP_WIFI_NAN_NPK_LEN ESP_WIFI_NAN_NDP_PMK_LEN /**< Length of NAN Pairwise Key (same as NDP PMK) */
|
||||
#define ESP_WIFI_NAN_NDP_PMKID_LEN 16 /**< Length of NAN Datapath PMKID */
|
||||
#define ESP_WIFI_NAN_MAX_PEER_CREDS 2 /**< <Maximum number of NAN peer credentials that can be stored */
|
||||
#define ESP_WIFI_NAN_MAX_CREDS_PER_SVC 4 /**< Maximum number of NAN security credentials per service (passphrase/PMK entries) */
|
||||
|
||||
#define ESP_WIFI_MAX_SVC_NAME_LEN 256 /**< Maximum length of NAN service name */
|
||||
|
||||
@@ -604,9 +604,8 @@ typedef struct {
|
||||
uint8_t scan_time; /**< Scan time in seconds while searching for a NAN cluster */
|
||||
uint16_t warm_up_sec; /**< Warm up time before assuming NAN Anchor Master role */
|
||||
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
||||
uint8_t nik[ESP_WIFI_NAN_NIK_LEN]; /**< Optional NIK. Auto-generated when nik_valid is false. */
|
||||
uint8_t nik_valid: 1; /**< NIK present in nik[] and should be used as-is. */
|
||||
uint8_t reserved: 7; /**< Reserved for future use. */
|
||||
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
||||
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
||||
} wifi_nan_sync_config_t;
|
||||
|
||||
/**
|
||||
@@ -874,7 +873,9 @@ typedef struct {
|
||||
#define ESP_WIFI_NDP_ROLE_RESPONDER 2 /**< Responder role for NAN Data Path */
|
||||
|
||||
#define ESP_WIFI_NAN_NDP_PMK_LEN 32 /**< Length of NAN Datapath PMK */
|
||||
#define ESP_WIFI_NAN_NPK_LEN ESP_WIFI_NAN_NDP_PMK_LEN /**< Length of NAN Pairwise Key (same as NDP PMK) */
|
||||
#define ESP_WIFI_NAN_NDP_PMKID_LEN 16 /**< Length of NAN Datapath PMKID */
|
||||
#define ESP_WIFI_NAN_MAX_PEER_CREDS 2 /**< Maximum number of NAN peer credentials that can be stored */
|
||||
#define ESP_WIFI_NAN_MAX_CREDS_PER_SVC 4 /**< Maximum number of NAN security credentials per service (passphrase/PMK entries) */
|
||||
|
||||
#define ESP_WIFI_MAX_SVC_NAME_LEN 256 /**< Maximum length of NAN service name */
|
||||
|
||||
@@ -22,7 +22,8 @@ extern "C" {
|
||||
.scan_time = 3, \
|
||||
.warm_up_sec = 5, \
|
||||
.disable_random_mac = false, \
|
||||
.nik_valid = false, \
|
||||
.reset_current_nvs_creds = false, \
|
||||
.use_nvs_for_caching = false, \
|
||||
};
|
||||
|
||||
#define NDP_STATUS_ACCEPTED 1
|
||||
|
||||
@@ -1517,16 +1517,33 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
||||
return ESP_OK;
|
||||
}
|
||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||
if (nan_cfg->nik_valid) {
|
||||
memcpy(s_nan_ctx.own_nik, nan_cfg->nik, ESP_WIFI_NAN_NIK_LEN);
|
||||
s_nan_ctx.own_nik_valid = true;
|
||||
} else {
|
||||
s_nan_ctx.own_nik_valid = false;
|
||||
s_nan_ctx.num_peer_creds = 0;
|
||||
memset(s_nan_ctx.peer_creds, 0, sizeof(s_nan_ctx.peer_creds));
|
||||
s_nan_ctx.use_nvs_for_caching = nan_cfg->use_nvs_for_caching;
|
||||
|
||||
if (nan_cfg->reset_current_nvs_creds) {
|
||||
/* Start from a clean slate: drop every credential persisted in NVS. */
|
||||
esp_wifi_nan_erase_all_creds();
|
||||
} else if (esp_wifi_nan_load_saved_creds(s_nan_ctx.own_nik, &s_nan_ctx.own_nik_valid,
|
||||
s_nan_ctx.peer_creds, &s_nan_ctx.num_peer_creds) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Failed to load saved NAN credentials");
|
||||
s_nan_ctx.own_nik_valid = false;
|
||||
s_nan_ctx.num_peer_creds = 0;
|
||||
}
|
||||
|
||||
if (!s_nan_ctx.own_nik_valid) {
|
||||
if (os_get_random(s_nan_ctx.own_nik, ESP_WIFI_NAN_NIK_LEN) != 0) {
|
||||
NAN_DATA_UNLOCK();
|
||||
ESP_LOGE(TAG, "Failed to generate NAN NIK");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
s_nan_ctx.own_nik_valid = true;
|
||||
/* Persist the freshly generated NIK only when NVS caching is enabled;
|
||||
* otherwise the identity stays ephemeral for this session. */
|
||||
if (s_nan_ctx.use_nvs_for_caching) {
|
||||
esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
|
||||
}
|
||||
}
|
||||
/* Drop the cached NIRA tag; it was derived from the previous NIK. */
|
||||
s_nan_ctx.nira_cached = false;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "esp_wifi_types_generic.h"
|
||||
#include "esp_private/wifi.h"
|
||||
#include "esp_nan.h"
|
||||
#include "utils/common.h" /* u8/u16 typedefs required by esp_wifi_driver.h */
|
||||
#include "esp_wifi_driver.h" /* wifi_nan_peer_creds_t + NAN credential NVS APIs */
|
||||
#include "os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -296,6 +298,12 @@ typedef struct {
|
||||
uint8_t cached_nira_nonce[8];
|
||||
uint8_t cached_nira_tag[8];
|
||||
bool nira_cached;
|
||||
/* Peer NIK/NPK credentials loaded from NVS at start and refreshed as peers
|
||||
* are paired. Drives NIRA identity resolution; optionally persisted to NVS
|
||||
* when @c use_nvs_for_caching is set. */
|
||||
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS];
|
||||
uint8_t num_peer_creds;
|
||||
bool use_nvs_for_caching;
|
||||
#endif
|
||||
#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT
|
||||
struct nan_pasn_data *nan_pasn_data;
|
||||
|
||||
@@ -847,6 +847,39 @@ static void nan_pairing_key_installed_cb(const uint8_t *peer_nmi,
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert or refresh a (peer NIK, NPK) entry in the in-RAM credential cache used
|
||||
* for NIRA identity resolution. Caller holds NAN_DATA_LOCK. A @a npk of NULL
|
||||
* stores a zeroed key. When the cache is full the oldest entry (slot 0) is
|
||||
* reused. */
|
||||
static void nan_app_update_peer_creds(const uint8_t *peer_nik, const uint8_t *npk)
|
||||
{
|
||||
wifi_nan_peer_creds_t *slot = NULL;
|
||||
|
||||
/* Reuse the slot already holding this NIK, if any. */
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (s_nan_ctx.peer_creds[i].is_valid &&
|
||||
os_memcmp(s_nan_ctx.peer_creds[i].peer_nik, peer_nik, ESP_WIFI_NAN_NIK_LEN) == 0) {
|
||||
slot = &s_nan_ctx.peer_creds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!slot) {
|
||||
if (s_nan_ctx.num_peer_creds < ESP_WIFI_NAN_MAX_PEER_CREDS) {
|
||||
slot = &s_nan_ctx.peer_creds[s_nan_ctx.num_peer_creds++];
|
||||
} else {
|
||||
slot = &s_nan_ctx.peer_creds[0];
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(slot->peer_nik, peer_nik, ESP_WIFI_NAN_NIK_LEN);
|
||||
if (npk) {
|
||||
memcpy(slot->npk, npk, ESP_WIFI_NAN_NPK_LEN);
|
||||
} else {
|
||||
memset(slot->npk, 0, ESP_WIFI_NAN_NPK_LEN);
|
||||
}
|
||||
slot->is_valid = true;
|
||||
}
|
||||
|
||||
void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
const uint8_t *peer_mac,
|
||||
const uint8_t *shared_key_attr,
|
||||
@@ -891,6 +924,8 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
bool already_had_nik = false;
|
||||
bool pairing_completed = false;
|
||||
uint8_t own_svc_id_to_disable = 0;
|
||||
bool persist_creds = false;
|
||||
uint8_t persist_npk[ESP_WIFI_NAN_NPK_LEN] = {0};
|
||||
|
||||
NAN_DATA_LOCK();
|
||||
struct peer_svc_info *p_peer_svc = nan_find_peer_svc_exact(svc_id, peer_svc_id, peer_mac);
|
||||
@@ -903,6 +938,15 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
ESP_LOGI(TAG, "Stored peer NIK from " MACSTR " (cipher_ver=%u, lifetime=%u s)",
|
||||
MAC2STR(peer_mac), cipher_ver, lifetime_sec);
|
||||
|
||||
/* Refresh the NIRA credential cache with this peer's NIK and, if the
|
||||
* pairing record is available, its NPK. */
|
||||
const struct nan_paired_peer *paired = nan_app_find_paired_peer(peer_mac);
|
||||
if (paired) {
|
||||
memcpy(persist_npk, paired->nd_pmk, ESP_WIFI_NAN_NPK_LEN);
|
||||
}
|
||||
nan_app_update_peer_creds(nik, paired ? paired->nd_pmk : NULL);
|
||||
persist_creds = s_nan_ctx.use_nvs_for_caching;
|
||||
|
||||
if (!already_had_nik) {
|
||||
pairing_completed = true;
|
||||
own_svc_id_to_disable = p_peer_svc->own_svc_id;
|
||||
@@ -910,6 +954,11 @@ void nan_app_receive_pairing_followup(uint8_t svc_id, uint8_t peer_svc_id,
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
/* Persist outside the lock; NVS writes can block. */
|
||||
if (persist_creds) {
|
||||
esp_wifi_nan_save_creds_for_peer(nik, persist_npk);
|
||||
}
|
||||
|
||||
/* Invoke blocking calls outside NAN_DATA_LOCK to avoid deadlock. */
|
||||
if (pairing_completed) {
|
||||
wifi_event_nan_pairing_complete_t evt = {0};
|
||||
@@ -959,8 +1008,7 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
|
||||
uint8_t expected_tag[NAN_NIRA_TAG_LEN];
|
||||
const uint8_t *nonce;
|
||||
const uint8_t *received_tag;
|
||||
struct peer_svc_info *p_peer_svc;
|
||||
bool match;
|
||||
bool match = false;
|
||||
|
||||
if (!peer_mac || !nira_attr) {
|
||||
return false;
|
||||
@@ -975,26 +1023,28 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
|
||||
nonce = nira_attr + 4;
|
||||
received_tag = nira_attr + 4 + NAN_NIRA_NONCE_LEN;
|
||||
|
||||
/* The peer derives its NIRA tag from one of its NIKs; the sending MAC may be
|
||||
* randomised, so resolve the identity by trying every cached peer NIK. */
|
||||
NAN_DATA_LOCK();
|
||||
p_peer_svc = nan_find_peer_svc(0, 0, peer_mac);
|
||||
if (!p_peer_svc || !p_peer_svc->has_nik) {
|
||||
NAN_DATA_UNLOCK();
|
||||
ESP_LOGD(TAG, "NIRA verify: no stored NIK for "MACSTR, MAC2STR(peer_mac));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nan_pairing_derive_nira_tag(p_peer_svc->peer_nik, peer_mac, nonce, expected_tag) != 0) {
|
||||
NAN_DATA_UNLOCK();
|
||||
ESP_LOGE(TAG, "NIRA verify: tag derivation failed for "MACSTR, MAC2STR(peer_mac));
|
||||
return false;
|
||||
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
|
||||
if (!s_nan_ctx.peer_creds[i].is_valid) {
|
||||
continue;
|
||||
}
|
||||
if (nan_pairing_derive_nira_tag(s_nan_ctx.peer_creds[i].peer_nik, peer_mac,
|
||||
nonce, expected_tag) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (os_memcmp_const(expected_tag, received_tag, NAN_NIRA_TAG_LEN) == 0) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
NAN_DATA_UNLOCK();
|
||||
|
||||
match = (os_memcmp_const(expected_tag, received_tag, NAN_NIRA_TAG_LEN) == 0);
|
||||
if (match) {
|
||||
ESP_LOGD(TAG, "NIRA verify: OK for "MACSTR, MAC2STR(peer_mac));
|
||||
} else {
|
||||
ESP_LOGW(TAG, "NIRA verify: tag mismatch for "MACSTR, MAC2STR(peer_mac));
|
||||
ESP_LOGW(TAG, "NIRA verify: no matching NIK for "MACSTR, MAC2STR(peer_mac));
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,11 @@ endif()
|
||||
if(CONFIG_ESP_WIFI_ENABLE_SAE_H2E)
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE_H2E)
|
||||
endif()
|
||||
if(CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT)
|
||||
# PASN reuses the SAE module (PWE/crypto + comeback-token mechanism), so
|
||||
# CONFIG_SAE must be defined whenever SoftAP-SAE or PASN is enabled. AP-side SAE
|
||||
# authentication paths are additionally gated on CONFIG_ESP_WIFI_SOFTAP_SUPPORT
|
||||
# in the sources so they stay out of PASN-only (SoftAP-disabled) builds.
|
||||
if(CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT OR CONFIG_ESP_WIFI_PASN_SUPPORT)
|
||||
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE)
|
||||
endif()
|
||||
if(CONFIG_ESP_WIFI_WPA3_COMPATIBLE_SUPPORT)
|
||||
|
||||
@@ -240,6 +240,12 @@ typedef enum {
|
||||
NAN_KEY_NM_TK,
|
||||
} nan_key_type_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t peer_nik[ESP_WIFI_NAN_NIK_LEN]; /**< Peer's NAN Identity Key (16 bytes) */
|
||||
uint8_t npk[ESP_WIFI_NAN_NPK_LEN]; /**< NAN Pairwise Key / NCS-SK PMK (32 bytes) */
|
||||
bool is_valid;
|
||||
} wifi_nan_peer_creds_t;
|
||||
|
||||
typedef wifi_scan_channel_bitmap_t channel_bitmap_t;
|
||||
|
||||
uint8_t *esp_wifi_ap_get_prof_pmk_internal(void);
|
||||
@@ -335,5 +341,11 @@ uint8_t esp_wifi_op_class_supported_internal(uint8_t op_class, uint8_t min_chan,
|
||||
bool esp_wifi_is_wpa3_compatible_mode_enabled(uint8_t if_index);
|
||||
uint8_t esp_wifi_ap_get_owe_config_internal(void);
|
||||
esp_err_t esp_nan_disable_pairing(uint8_t svc_id);
|
||||
esp_err_t esp_wifi_nan_load_saved_creds(uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN], bool *own_nik_valid,
|
||||
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS], uint8_t *num_peer_creds);
|
||||
esp_err_t esp_wifi_nan_save_own_nik(const uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN]) ;
|
||||
esp_err_t esp_wifi_nan_save_creds_for_peer(const uint8_t peer_nik[ESP_WIFI_NAN_NIK_LEN],
|
||||
const uint8_t npk[ESP_WIFI_NAN_NPK_LEN]);
|
||||
esp_err_t esp_wifi_nan_erase_all_creds(void);
|
||||
|
||||
#endif /* _ESP_WIFI_DRIVER_H_ */
|
||||
|
||||
@@ -441,7 +441,11 @@ void esp_wifi_unregister_wpa3_cb(void)
|
||||
}
|
||||
#endif /* CONFIG_WPA3_SAE */
|
||||
|
||||
#ifdef CONFIG_SAE
|
||||
/* AP-side (hostap) SAE authentication. CONFIG_SAE may also be enabled for PASN
|
||||
* with SoftAP disabled, but this block both defines and calls SoftAP-only
|
||||
* primitives (esp_send_sae_auth_reply, handle_auth_sae), so additionally gate
|
||||
* it on CONFIG_ESP_WIFI_SOFTAP_SUPPORT. */
|
||||
#if defined(CONFIG_SAE) && defined(CONFIG_ESP_WIFI_SOFTAP_SUPPORT)
|
||||
|
||||
static TaskHandle_t g_wpa3_hostap_task_hdl = NULL;
|
||||
static QueueHandle_t g_wpa3_hostap_evt_queue = NULL;
|
||||
@@ -861,4 +865,4 @@ void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb)
|
||||
wpa_cb->wpa3_hostap_handle_auth = wpa3_hostap_handle_auth;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SAE */
|
||||
#endif /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef ESP_WPA3_H
|
||||
#define ESP_WPA3_H
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_wifi_driver.h"
|
||||
|
||||
#ifdef CONFIG_WPA3_SAE
|
||||
@@ -27,7 +28,10 @@ static inline void esp_wpa3_free_sae_data(void)
|
||||
|
||||
#endif /* CONFIG_WPA3_SAE */
|
||||
|
||||
#ifdef CONFIG_SAE
|
||||
/* AP-side (hostap) SAE definitions require SoftAP; CONFIG_SAE alone may be set
|
||||
* for PASN. The #else branch provides a no-op esp_wifi_register_wpa3_ap_cb()
|
||||
* stub so callers link in PASN-only (SoftAP-disabled) builds. */
|
||||
#if defined(CONFIG_SAE) && defined(CONFIG_ESP_WIFI_SOFTAP_SUPPORT)
|
||||
enum SIG_WPA3_TASK {
|
||||
SIG_WPA3_RX_COMMIT,
|
||||
SIG_WPA3_RX_CONFIRM,
|
||||
@@ -58,12 +62,12 @@ void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb);
|
||||
int wpa3_hostap_auth_init(void *data);
|
||||
bool wpa3_hostap_auth_deinit(void);
|
||||
|
||||
#else /* CONFIG_SAE */
|
||||
#else /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
|
||||
|
||||
static inline void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb)
|
||||
{
|
||||
wpa_cb->wpa3_hostap_handle_auth = NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SAE */
|
||||
#endif /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
|
||||
#endif /* ESP_WPA3_H */
|
||||
|
||||
@@ -28,7 +28,10 @@
|
||||
#define OWE_DH_GRP19 19
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SAE
|
||||
/* AP-side SAE authentication. Requires SoftAP: CONFIG_SAE may also be enabled
|
||||
* for PASN (SoftAP disabled), but this block calls SoftAP-only primitives
|
||||
* (e.g. esp_send_sae_auth_reply), so gate it on CONFIG_ESP_WIFI_SOFTAP_SUPPORT. */
|
||||
#if defined(CONFIG_SAE) && defined(CONFIG_ESP_WIFI_SOFTAP_SUPPORT)
|
||||
|
||||
static void sae_set_state(struct sta_info *sta, enum sae_state state,
|
||||
const char *reason)
|
||||
@@ -778,7 +781,7 @@ queued:
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SAE */
|
||||
#endif /* CONFIG_SAE && CONFIG_ESP_WIFI_SOFTAP_SUPPORT */
|
||||
|
||||
u16 wpa_res_to_status_code(enum wpa_validate_result res)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user