Implemented wifi credentials storage (#11)

* implemented wifi credentials storage

* encrypt wifi credentials
This commit is contained in:
Ken Van Hoeylandt 2024-01-14 18:46:04 +01:00 committed by GitHub
parent 069416eee5
commit ba1f81f599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 642 additions and 48 deletions

View File

@ -1,5 +1,5 @@
idf_component_register(
SRC_DIRS "src"
INCLUDE_DIRS "src"
REQUIRES mlib cmsis_core
REQUIRES mlib cmsis_core mbedtls esp_hw_support nvs_flash
)

View File

@ -21,22 +21,22 @@ DICT_DEF2(AppManifestDict, const char*, M_CSTR_DUP_OPLIST, const AppManifest*, M
}
AppManifestDict_t app_manifest_dict;
Mutex* mutex = NULL;
Mutex* hash_mutex = NULL;
void tt_app_manifest_registry_init() {
tt_assert(mutex == NULL);
mutex = tt_mutex_alloc(MutexTypeNormal);
tt_assert(hash_mutex == NULL);
hash_mutex = tt_mutex_alloc(MutexTypeNormal);
AppManifestDict_init(app_manifest_dict);
}
void app_registry_lock() {
tt_assert(mutex != NULL);
tt_mutex_acquire(mutex, TtWaitForever);
tt_assert(hash_mutex != NULL);
tt_mutex_acquire(hash_mutex, TtWaitForever);
}
void app_registry_unlock() {
tt_assert(mutex != NULL);
tt_mutex_release(mutex);
tt_assert(hash_mutex != NULL);
tt_mutex_release(hash_mutex);
}
void tt_app_manifest_registry_add(const AppManifest _Nonnull* manifest) {

View File

@ -0,0 +1,11 @@
#include "hash.h"
uint32_t tt_hash_string_djb2(const char* str) {
uint32_t hash = 5381;
char c = (char)*str++;
while (c != 0) {
hash = ((hash << 5) + hash) + (uint32_t)c; // hash * 33 + c
c = (char)*str++;
}
return hash;
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* This is quicker than the m-string.h hashing, as the latter
* operates on raw memory blocks and thus a strlen() call is required first.
* @param[in] str the string to calculate the hash for
* @return the hash
*/
uint32_t tt_hash_string_djb2(const char* str);
#ifdef __cplusplus
}
#endif

View File

@ -83,14 +83,6 @@ void tt_delay_tick(uint32_t ticks);
*/
TtStatus tt_delay_until_tick(uint32_t tick);
/** Get current tick counter
*
* System uptime, may overflow.
*
* @return Current ticks in milliseconds
*/
uint32_t tt_get_tick(void);
/** Convert milliseconds to ticks
*
* @param[in] milliseconds time in milliseconds

View File

@ -0,0 +1,155 @@
#include "secure.h"
#include "aes/esp_aes.h"
#include "check.h"
#include "esp_mac.h"
#include "esp_cpu.h"
#include "log.h"
#include "nvs_flash.h"
#include <string.h>
#define TAG "secure"
#define TT_NVS_NAMESPACE "tt_secure"
/**
* Get a key based on hardware parameters.
* @param[out] key the output key
*/
static void get_hardware_key(uint8_t key[32]) {
uint8_t mac[8];
// MAC can be 6 or 8 bytes
size_t mac_length = esp_mac_addr_len_get(ESP_MAC_EFUSE_FACTORY);
TT_LOG_I(TAG, "Using MAC with length %u", mac_length);
tt_check(mac_length <= 8);
ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_EFUSE_FACTORY));
// Fill buffer with repeating MAC
for (size_t i = 0; i < 32; ++i) {
key[i] = mac[i % mac_length];
}
}
/**
* The key is built up as follows:
* - Fetch 32 bytes from NVS storage and store as key data
* - Fetch 6-8 MAC bytes and overwrite the first 6-8 bytes of the key with this info
*
* When flash encryption is disabled:
* Without the MAC data, an attack would look like this:
* - Retrieve all the partitions from the ESP32
* - Read the key from NVS flash
* - Use the key to decrypt
* With the MAC data added, an attacker would have to do much more:
* - Retrieve all the partitions from the ESP32 (copy app)
* - Upload custom app to retrieve internal MAC
* - Read the key from NVS flash
* - Re-flash original app and combine it with the MAC
* - Use the key to decrypt
* - Re-flash the device with original firmware.
*
* Adding the MAC doesn't add a lot of extra security, but I think it's worth it.
*
* @param[out] key the output key
*/
static void get_nvs_key(uint8_t key[32]) {
nvs_handle_t handle;
esp_err_t result = nvs_open(TT_NVS_NAMESPACE, NVS_READWRITE, &handle);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to get key from NVS (%s)", esp_err_to_name(result));
tt_crash();
}
size_t length = 32;
if (nvs_get_blob(handle, "key", key, &length) == ESP_OK) {
TT_LOG_I(TAG, "Fetched key from NVS (%d bytes)", length);
tt_check(length == 32);
} else {
esp_cpu_cycle_count_t cycle_count = esp_cpu_get_cycle_count();
unsigned seed = (unsigned)cycle_count;
for (int i = 0; i < 32; ++i) {
key[i] = (uint8_t)rand_r(&seed);
}
ESP_ERROR_CHECK(nvs_set_blob(handle, "key", key, 32));
TT_LOG_I(TAG, "Stored new key in NVS");
}
nvs_close(handle);
}
/**
* Performs XOR on 2 memory regions and stores it in a third
* @param[in] in_left input buffer for XOR
* @param[in] in_right second input buffer for XOR
* @param[out] out output buffer for result of XOR
* @param[in] length data length (all buffers must be at least this size)
*/
static void xor_key(const uint8_t* in_left, const uint8_t* in_right, uint8_t* out, size_t length) {
for (int i = 0; i < length; ++i) {
out[i] = in_left[i] ^ in_right[i];
}
}
/**
* Combines a stored key and a hardware key into a single reliable key value.
* @param[out] key the key output
*/
static void get_key(uint8_t key[32]) {
#if !defined(CONFIG_SECURE_BOOT) || !defined(CONFIG_SECURE_FLASH_ENC_ENABLED)
TT_LOG_W(TAG, "Using tt_secure_* code with secure boot and/or flash encryption disabled.");
TT_LOG_W(TAG, "An attacker with physical access to your ESP32 can decrypt your secure data.");
#endif
uint8_t hardware_key[32];
uint8_t nvs_key[32];
get_hardware_key(hardware_key);
get_nvs_key(nvs_key);
xor_key(hardware_key, nvs_key, key, 32);
}
void tt_secure_get_iv_from_string(const char* input, uint8_t iv[16]) {
memset((void*)iv, 0, 16);
char c = *input++;
int index = 0;
printf("IV: ");
while (c) {
printf(" %0X:%02d", c, index);
iv[index] = c;
index++;
c = *input++;
}
printf("\n");
}
int tt_secure_encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length) {
tt_check(length % 16 == 0, "Length is not a multiple of 16 bytes (for AES 256");
uint8_t key[32];
get_key(key);
uint8_t iv_copy[16];
memcpy(iv_copy, iv, sizeof(iv_copy));
esp_aes_context ctx;
esp_aes_init(&ctx);
esp_aes_setkey(&ctx, key, 256);
int result = esp_aes_crypt_cbc(&ctx, ESP_AES_ENCRYPT, length, iv_copy, in_data, out_data);
esp_aes_free(&ctx);
return result;
}
int tt_secure_decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length) {
tt_check(length % 16 == 0, "Length is not a multiple of 16 bytes (for AES 256");
uint8_t key[32];
get_key(key);
uint8_t iv_copy[16];
memcpy(iv_copy, iv, sizeof(iv_copy));
esp_aes_context ctx;
esp_aes_init(&ctx);
esp_aes_setkey(&ctx, key, 256);
int result = esp_aes_crypt_cbc(&ctx, ESP_AES_DECRYPT, length, iv_copy, in_data, out_data);
esp_aes_free(&ctx);
return result;
}

View File

@ -0,0 +1,64 @@
/** @file secure.h
*
* @brief Hardware-bound encryption methods.
* @warning Enable secure boot and flash encryption to increase security.
*
* Offers AES 256 CBC encryption with built-in key.
* The key is built from data including:
* - the internal factory MAC address
* - random data stored in NVS
*
* It's important to use flash encryption to avoid an attacker to get
* access to your encrypted data. If flash encryption is disabled,
* someone can fetch the key from the partitions.
*
* See:
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/secure-boot-v2.html
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html
*/
#pragma once
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Fills the IV with zeros and then copies up to 16 characters of the string into the IV.
* @param input input text
* @param iv output IV
*/
void tt_secure_get_iv_from_string(const char* input, uint8_t iv[16]);
/**
* @brief Encrypt data.
*
* Important: Use flash encryption to increase security.
* Important: input and output data must be aligned to 16 bytes.
*
* @param iv the AES IV
* @param data_in input data
* @param data_out output data
* @param length data length, a multiple of 16
* @return the result of esp_aes_crypt_cbc() (MBEDTLS_ERR_*)
*/
int tt_secure_encrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length);
/**
* @brief Decrypt data.
*
* Important: Use flash encryption to increase security.
* Important: input and output data must be aligned to 16 bytes.
*
* @param iv AES IV
* @param data_in input data
* @param data_out output data
* @param length data length, a multiple of 16
* @return the result of esp_aes_crypt_cbc() (MBEDTLS_ERR_*)
*/
int tt_secure_decrypt(const uint8_t iv[16], uint8_t* in_data, uint8_t* out_data, size_t length);
#ifdef __cplusplus
}
#endif

View File

@ -3,6 +3,7 @@
struct TtString {
string_t string;
// TODO store optional hash for quick string comparison
};
#undef tt_string_alloc_set

View File

@ -6,6 +6,8 @@
#include "tactility_core.h"
#include "wifi_connect_state_updating.h"
#define TAG "wifi_connect"
// Forward declarations
static void wifi_connect_event_callback(const void* message, void* context);
@ -55,9 +57,12 @@ void wifi_connect_unlock(WifiConnect* wifi) {
void wifi_connect_request_view_update(WifiConnect* wifi) {
wifi_connect_lock(wifi);
if (wifi->view_enabled) {
lvgl_port_lock(100);
wifi_connect_view_update(&wifi->view, &wifi->bindings, &wifi->state);
lvgl_port_unlock();
if (lvgl_port_lock(1000)) {
wifi_connect_view_update(&wifi->view, &wifi->bindings, &wifi->state);
lvgl_port_unlock();
} else {
TT_LOG_E(TAG, "failed to lock lvgl");
}
}
wifi_connect_unlock(wifi);
}
@ -87,6 +92,8 @@ static void app_show(App app, lv_obj_t* parent) {
static void app_hide(App app) {
WifiConnect* wifi = (WifiConnect*)tt_app_get_data(app);
// No need to lock view, as this is called from within Gui's LVGL context
wifi_connect_view_destroy(&wifi->view);
wifi_connect_lock(wifi);
wifi->view_enabled = false;
wifi_connect_unlock(wifi);

View File

@ -2,7 +2,7 @@
#include <stdbool.h>
typedef void (*OnConnectSsid)(const char* ssid, const char* password, void* context);
typedef void (*OnConnectSsid)(const char* ssid, const char password[64], void* context);
typedef struct {
OnConnectSsid on_connect_ssid;

View File

@ -1,24 +1,54 @@
#include "wifi_connect_view.h"
#include "log.h"
#include "lvgl.h"
#include "services/wifi/wifi_credentials.h"
#include "ui/spacer.h"
#include "ui/style.h"
#include "wifi_connect.h"
#include "wifi_connect_bundle.h"
#include "wifi_connect_state.h"
#define TAG "wifi_connect"
static void show_keyboard(lv_event_t* event) {
WifiConnectView* view = (WifiConnectView*)event->user_data;
lv_obj_clear_flag(view->keyboard, LV_OBJ_FLAG_HIDDEN);
lv_keyboard_set_textarea(view->keyboard, event->current_target);
// TODO: This doesn't work yet as most content is not scrollable
lv_obj_scroll_to_view(event->current_target, LV_ANIM_OFF);
}
static void hide_keyboard(lv_event_t* event) {
WifiConnectView* view = (WifiConnectView*)event->user_data;
lv_obj_add_flag(view->keyboard, LV_OBJ_FLAG_HIDDEN);
lv_keyboard_set_textarea(view->keyboard, event->current_target);
}
static void on_connect(lv_event_t* event) {
WifiConnect* wifi = (WifiConnect*)event->user_data;
WifiConnectView* view = &wifi->view;
const char* ssid = lv_textarea_get_text(view->ssid_textarea);
const char* password = lv_textarea_get_text(view->password_textarea);
if (strlen(password) > 63) {
// TODO: UI feedback
TT_LOG_E(TAG, "Password too long");
return;
}
char password_buffer[64];
strcpy(password_buffer, password);
WifiConnectBindings* bindings = &wifi->bindings;
bindings->on_connect_ssid(
ssid,
password,
password_buffer,
bindings->on_connect_ssid_context
);
if (lv_obj_get_state(view->remember_switch) == LV_STATE_CHECKED) {
tt_wifi_credentials_set(ssid, password_buffer);
}
}
// TODO: Standardize dialogs
@ -47,6 +77,8 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) {
lv_textarea_set_password_show_time(view->password_textarea, 0);
lv_textarea_set_password_mode(view->password_textarea, true);
tt_lv_spacer_create(parent, 1, 2);
lv_obj_t* button_container = lv_obj_create(parent);
lv_obj_set_width(button_container, LV_PCT(100));
lv_obj_set_height(button_container, LV_SIZE_CONTENT);
@ -54,8 +86,16 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) {
lv_obj_set_style_border_width(button_container, 0, 0);
lv_obj_set_flex_flow(button_container, LV_FLEX_FLOW_ROW);
lv_obj_t* spacer_left = tt_lv_spacer_create(button_container, 1, 1);
lv_obj_set_flex_grow(spacer_left, 1);
view->remember_switch = lv_switch_create(button_container);
lv_obj_add_state(view->remember_switch, LV_STATE_CHECKED);
tt_lv_spacer_create(button_container, 2, 1);
lv_obj_t* remember_label = lv_label_create(button_container);
lv_label_set_text(remember_label, "Remember");
lv_obj_t* spacer_center = tt_lv_spacer_create(button_container, 1, 1);
lv_obj_set_flex_grow(spacer_center, 1);
view->connect_button = lv_btn_create(button_container);
lv_obj_t* connect_label = lv_label_create(view->connect_button);
@ -63,6 +103,16 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) {
lv_obj_center(connect_label);
lv_obj_add_event_cb(view->connect_button, &on_connect, LV_EVENT_CLICKED, wifi);
view->keyboard = lv_keyboard_create(lv_scr_act());
lv_obj_add_flag(view->keyboard, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_event_cb(view->ssid_textarea, show_keyboard, LV_EVENT_FOCUSED, view);
lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, view);
lv_obj_add_event_cb(view->ssid_textarea, hide_keyboard, LV_EVENT_READY, view);
lv_obj_add_event_cb(view->password_textarea, show_keyboard, LV_EVENT_FOCUSED, view);
lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_DEFOCUSED, view);
lv_obj_add_event_cb(view->password_textarea, hide_keyboard, LV_EVENT_READY, view);
// Init from app parameters
Bundle* _Nullable bundle = tt_app_get_parameters(app);
if (bundle) {
@ -78,6 +128,11 @@ void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent) {
}
}
void wifi_connect_view_destroy(WifiConnectView* view) {
lv_obj_del(view->keyboard);
view->keyboard = NULL;
}
void wifi_connect_view_update(WifiConnectView* view, WifiConnectBindings* bindings, WifiConnectState* state) {
UNUSED(view);
UNUSED(bindings);

View File

@ -14,10 +14,13 @@ typedef struct {
lv_obj_t* password_textarea;
lv_obj_t* connect_button;
lv_obj_t* cancel_button;
lv_obj_t* remember_switch;
lv_obj_t* keyboard;
} WifiConnectView;
void wifi_connect_view_create(App app, void* wifi, lv_obj_t* parent);
void wifi_connect_view_update(WifiConnectView* view, WifiConnectBindings* bindings, WifiConnectState* state);
void wifi_connect_view_destroy(WifiConnectView* view);
#ifdef __cplusplus
}

View File

@ -7,15 +7,25 @@
#include "tactility_core.h"
#include "wifi_manage_state_updating.h"
#include "wifi_manage_view.h"
#include "services/wifi/wifi_credentials.h"
#define TAG "wifi_manage"
// Forward declarations
static void wifi_manage_event_callback(const void* message, void* context);
static void on_connect(const char* ssid) {
Bundle bundle = tt_bundle_alloc();
tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_SSID, ssid);
tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, ""); // TODO: Implement from cache
loader_start_app("wifi_connect", false, bundle);
char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT];
if (tt_wifi_credentials_get(ssid, password)) {
TT_LOG_I(TAG, "Connecting with known credentials");
wifi_connect(ssid, password);
} else {
TT_LOG_I(TAG, "Starting connection dialog");
Bundle bundle = tt_bundle_alloc();
tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_SSID, ssid);
tt_bundle_put_string(bundle, WIFI_CONNECT_PARAM_PASSWORD, "");
loader_start_app("wifi_connect", false, bundle);
}
}
static void on_disconnect() {
@ -69,9 +79,12 @@ void wifi_manage_unlock(WifiManage* wifi) {
void wifi_manage_request_view_update(WifiManage* wifi) {
wifi_manage_lock(wifi);
if (wifi->view_enabled) {
lvgl_port_lock(100);
wifi_manage_view_update(&wifi->view, &wifi->bindings, &wifi->state);
lvgl_port_unlock();
if (lvgl_port_lock(1000)) {
wifi_manage_view_update(&wifi->view, &wifi->bindings, &wifi->state);
lvgl_port_unlock();
} else {
TT_LOG_E(TAG, "failed to lock lvgl");
}
}
wifi_manage_unlock(wifi);
}

View File

@ -70,7 +70,13 @@ void gui_hide_app() {
gui_lock();
ViewPort* view_port = gui->app_view_port;
tt_check(view_port != NULL);
// We must lock the LVGL port, because the viewport hide callbacks
// might call LVGL APIs (e.g. to remove the keyboard from the screen root)
tt_check(lvgl_port_lock(1000));
view_port_hide(view_port);
lvgl_port_unlock();
view_port_free(view_port);
gui->app_view_port = NULL;
gui_unlock();

View File

@ -55,21 +55,24 @@ void gui_redraw(Gui* gui) {
// Lock GUI and LVGL
gui_lock();
tt_check(lvgl_port_lock(100));
lv_obj_clean(gui->lvgl_parent);
if (lvgl_port_lock(1000)) {
lv_obj_clean(gui->lvgl_parent);
if (gui->app_view_port != NULL) {
ViewPort* view_port = gui->app_view_port;
tt_assert(view_port);
App app = gui->app_view_port->app;
lv_obj_t* container = create_app_views(gui->lvgl_parent, app);
view_port_show(view_port, container);
if (view_port!= NULL) {
App app = view_port->app;
lv_obj_t* container = create_app_views(gui->lvgl_parent, app);
view_port_show(view_port, container);
} else {
TT_LOG_W(TAG, "nothing to draw");
}
// Unlock GUI and LVGL
lvgl_port_unlock();
} else {
TT_LOG_W(TAG, "nothing to draw");
TT_LOG_E(TAG, "failed to lock lvgl");
}
// Unlock GUI and LVGL
lvgl_port_unlock();
gui_unlock();
}

View File

@ -119,14 +119,13 @@ bool wifi_is_scanning() {
return wifi_singleton->scan_active;
}
void wifi_connect(const char* ssid, const char* _Nullable password) {
void wifi_connect(const char* ssid, const char _Nullable password[64]) {
tt_assert(wifi_singleton);
tt_check(strlen(ssid) <= 32);
tt_check(password == NULL || strlen(password) <= 64);
WifiMessage message = {.type = WifiMessageTypeConnect};
memcpy(message.connect_message.ssid, ssid, 32);
if (password != NULL) {
memcpy(message.connect_message.password, password, 32);
memcpy(message.connect_message.password, password, 64);
} else {
message.connect_message.password[0] = 0;
}
@ -227,15 +226,17 @@ static void wifi_publish_event_simple(Wifi* wifi, WifiEventType type) {
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
UNUSED(arg);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
ESP_LOGI(TAG, "event_handler: sta start");
if (wifi_singleton->radio_state == WIFI_RADIO_CONNECTION_PENDING) {
esp_wifi_connect();
}
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
xEventGroupSetBits(wifi_singleton->event_group, WIFI_FAIL_BIT);
ESP_LOGI(TAG, "disconnected");
ESP_LOGI(TAG, "event_handler: disconnected");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "event_handler: got ip:" IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(wifi_singleton->event_group, WIFI_CONNECTED_BIT);
}
}

View File

@ -86,7 +86,7 @@ void wifi_set_enabled(bool enabled);
* @param ssid
* @param password
*/
void wifi_connect(const char* ssid, const char* _Nullable password);
void wifi_connect(const char* ssid, const char _Nullable password[64]);
/**
* @brief Disconnect from the access point. Doesn't have any effect when not connected.

View File

@ -0,0 +1,235 @@
#include "wifi_credentials.h"
#include "nvs_flash.h"
#include "log.h"
#include "hash.h"
#include "check.h"
#include "mutex.h"
#include "secure.h"
#define TAG "wifi_credentials"
#define TT_NVS_NAMESPACE "tt_wifi_cred" // limited by NVS_KEY_NAME_MAX_SIZE
#define TT_NVS_PARTITION "nvs"
static void tt_wifi_credentials_mutex_lock();
static void tt_wifi_credentials_mutex_unlock();
// region Hash
static Mutex* hash_mutex = NULL;
static int8_t ssid_hash_index = -1;
static uint32_t ssid_hashes[TT_WIFI_CREDENTIALS_LIMIT] = { 0 };
static int hash_find_value(uint32_t hash) {
tt_wifi_credentials_mutex_lock();
for (int i = 0; i < ssid_hash_index; ++i) {
if (ssid_hashes[i] == hash) {
return i;
}
}
tt_wifi_credentials_mutex_unlock();
return -1;
}
static int hash_find_string(const char* ssid) {
uint32_t hash = tt_hash_string_djb2(ssid);
return hash_find_value(hash);
}
static bool hash_contains_string(const char* ssid) {
return hash_find_string(ssid) != -1;
}
static bool hash_contains_value(uint32_t value) {
return hash_find_value(value) != -1;
}
static void hash_add(const char* ssid) {
uint32_t hash = tt_hash_string_djb2(ssid);
if (!hash_contains_value(hash)) {
tt_wifi_credentials_mutex_lock();
tt_check((ssid_hash_index + 1) < TT_WIFI_CREDENTIALS_LIMIT, "exceeding wifi credentials list size");
ssid_hash_index++;
ssid_hashes[ssid_hash_index] = hash;
tt_wifi_credentials_mutex_unlock();
}
}
static void hash_reset_all() {
ssid_hash_index = -1;
}
// endregion Hash
// region Wi-Fi Credentials - static
static void tt_wifi_credentials_mutex_lock() {
tt_mutex_acquire(hash_mutex, TtWaitForever);
}
static void tt_wifi_credentials_mutex_unlock() {
tt_mutex_release(hash_mutex);
}
static esp_err_t tt_wifi_credentials_nvs_open(nvs_handle_t* handle, nvs_open_mode_t mode) {
return nvs_open(TT_NVS_NAMESPACE, NVS_READWRITE, handle);
}
static void tt_wifi_credentials_nvs_close(nvs_handle_t handle) {
nvs_close(handle);
}
static bool tt_wifi_credentials_contains_in_flash(const char* ssid) {
nvs_handle_t handle;
esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READONLY);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to open NVS handle: %s", esp_err_to_name(result));
return false;
}
hash_reset_all();
nvs_iterator_t iterator;
result = nvs_entry_find(TT_NVS_PARTITION, TT_NVS_NAMESPACE, NVS_TYPE_BLOB, &iterator);
bool contains_ssid = false;
while (result == ESP_OK) {
nvs_entry_info_t info;
nvs_entry_info(iterator, &info); // Can omit error check if parameters are guaranteed to be non-NULL
if (strcmp(info.key, ssid) == 0) {
contains_ssid = true;
break;
}
result = nvs_entry_next(&iterator);
}
nvs_release_iterator(iterator);
tt_wifi_credentials_nvs_close(handle);
return contains_ssid;
}
// endregion Wi-Fi Credentials - static
// region Wi-Fi Credentials - public
bool tt_wifi_credentials_contains(const char* ssid) {
uint32_t hash = tt_hash_string_djb2(ssid);
if (hash_contains_value(hash)) {
return tt_wifi_credentials_contains_in_flash(ssid);
} else {
return false;
}
}
void tt_wifi_credentials_init() {
hash_reset_all();
if (hash_mutex == NULL) {
hash_mutex = tt_mutex_alloc(MutexTypeRecursive);
}
nvs_handle_t handle;
esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READWRITE);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to open NVS handle for init: %s", esp_err_to_name(result));
return;
}
nvs_iterator_t iterator;
result = nvs_entry_find(TT_NVS_PARTITION, TT_NVS_NAMESPACE, NVS_TYPE_BLOB, &iterator);
while (result == ESP_OK) {
nvs_entry_info_t info;
nvs_entry_info(iterator, &info); // Can omit error check if parameters are guaranteed to be non-NULL
hash_add(info.key);
result = nvs_entry_next(&iterator);
}
nvs_release_iterator(iterator);
tt_wifi_credentials_nvs_close(handle);
}
bool tt_wifi_credentials_get(const char* ssid, char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT]) {
nvs_handle_t handle;
esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READONLY);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to open NVS handle: %s", esp_err_to_name(result));
return false;
}
char password_encrypted[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT];
size_t length = TT_WIFI_CREDENTIALS_PASSWORD_LIMIT;
result = nvs_get_blob(handle, ssid, password_encrypted, &length);
uint8_t iv[16];
tt_secure_get_iv_from_string(ssid, iv);
int decrypt_result = tt_secure_decrypt(
iv,
(uint8_t*)password_encrypted,
(uint8_t*)password,
TT_WIFI_CREDENTIALS_PASSWORD_LIMIT
);
if (decrypt_result != 0) {
result = ESP_FAIL;
TT_LOG_E(TAG, "Failed to decrypt credentials for \"%s\": %d", ssid, decrypt_result);
}
if (result != ESP_OK && result != ESP_ERR_NVS_NOT_FOUND) {
TT_LOG_E(TAG, "Failed to get credentials for \"%s\": %s", ssid, esp_err_to_name(result));
}
tt_wifi_credentials_nvs_close(handle);
return result == ESP_OK;
}
bool tt_wifi_credentials_set(const char* ssid, char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT]) {
nvs_handle_t handle;
esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READWRITE);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to open NVS handle: %s", esp_err_to_name(result));
return false;
}
char password_encrypted[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT];
uint8_t iv[16];
tt_secure_get_iv_from_string(ssid, iv);
int encrypt_result = tt_secure_encrypt(
iv,
(uint8_t*)password,
(uint8_t*)password_encrypted,
TT_WIFI_CREDENTIALS_PASSWORD_LIMIT
);
if (encrypt_result != 0) {
result = ESP_FAIL;
TT_LOG_E(TAG, "Failed to encrypt credentials for \"%s\": %d", ssid, encrypt_result);
}
result = nvs_set_blob(handle, ssid, password_encrypted, TT_WIFI_CREDENTIALS_PASSWORD_LIMIT);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to get credentials for \"%s\": %s", ssid, esp_err_to_name(result));
}
tt_wifi_credentials_nvs_close(handle);
return result == ESP_OK;
}
bool tt_wifi_credentials_remove(const char* ssid) {
nvs_handle_t handle;
esp_err_t result = tt_wifi_credentials_nvs_open(&handle, NVS_READWRITE);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to open NVS handle to store \"%s\": %s", ssid, esp_err_to_name(result));
return false;
}
result = nvs_erase_key(handle, ssid);
if (result != ESP_OK) {
TT_LOG_E(TAG, "Failed to erase credentials for \"%s\": %s", ssid, esp_err_to_name(result));
}
tt_wifi_credentials_nvs_close(handle);
return result == ESP_OK;
}
// end region Wi-Fi Credentials - public

View File

@ -0,0 +1,25 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define TT_WIFI_CREDENTIALS_PASSWORD_LIMIT 64 // Should be equal to wifi_sta_config_t.password
// TODO: Move to config file
#define TT_WIFI_CREDENTIALS_LIMIT 16
void tt_wifi_credentials_init();
bool tt_wifi_credentials_contains(const char* ssid);
bool tt_wifi_credentials_get(const char* ssid, char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT]);
bool tt_wifi_credentials_set(const char* ssid, char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT]);
bool tt_wifi_credentials_remove(const char* ssid);
#ifdef __cplusplus
}
#endif

View File

@ -9,6 +9,7 @@
#include "partitions.h"
#include "service_registry.h"
#include "services/loader/loader.h"
#include "services/wifi/wifi_credentials.h"
#include <sys/cdefs.h>
#define TAG "tactility"
@ -81,12 +82,15 @@ __attribute__((unused)) extern void tactility_start(const Config* _Nonnull confi
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
TT_LOG_I(TAG, "nvs erasing");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
TT_LOG_I(TAG, "nvs initialized");
tt_partitions_init();
tt_wifi_credentials_init();
Hardware hardware = tt_hardware_init(config->hardware);
/*NbLvgl lvgl =*/tt_graphics_init(&hardware);