Skip to content

Commit

Permalink
implemented wifi credentials storage
Browse files Browse the repository at this point in the history
  • Loading branch information
KenVanHoeylandt committed Jan 14, 2024
1 parent 069416e commit 9104622
Show file tree
Hide file tree
Showing 20 changed files with 589 additions and 48 deletions.
2 changes: 1 addition & 1 deletion components/tactility-core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
14 changes: 7 additions & 7 deletions components/tactility-core/src/app_manifest_registry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
11 changes: 11 additions & 0 deletions components/tactility-core/src/hash.c
Original file line number Diff line number Diff line change
@@ -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;
}
19 changes: 19 additions & 0 deletions components/tactility-core/src/hash.h
Original file line number Diff line number Diff line change
@@ -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
8 changes: 0 additions & 8 deletions components/tactility-core/src/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
141 changes: 141 additions & 0 deletions components/tactility-core/src/secure.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#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);
}

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;
}
57 changes: 57 additions & 0 deletions components/tactility-core/src/secure.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/** @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 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
1 change: 1 addition & 0 deletions components/tactility-core/src/tt_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

struct TtString {
string_t string;
// TODO store optional hash for quick string comparison
};

#undef tt_string_alloc_set
Expand Down
13 changes: 10 additions & 3 deletions components/tactility/src/apps/system/wifi_connect/wifi_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit 9104622

Please sign in to comment.