Implemented wifi credentials storage (#11)
* implemented wifi credentials storage * encrypt wifi credentials
This commit is contained in:
committed by
GitHub
parent
069416eee5
commit
ba1f81f599
@@ -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
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
struct TtString {
|
||||
string_t string;
|
||||
// TODO store optional hash for quick string comparison
|
||||
};
|
||||
|
||||
#undef tt_string_alloc_set
|
||||
|
||||
Reference in New Issue
Block a user