From b179ce6884d02ec8a96d6c4660f74de5d1bda0aa Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Mon, 23 Jul 2018 09:46:45 +0200 Subject: [PATCH] Add provisioning task, refactor provisioning --- .gitignore | 1 + .vscode/settings.json | 8 + Kconfig | 71 ++++++ include/TheThingsNetwork.h | 11 +- src/TheThingsNetwork.cpp | 252 ++------------------- src/hal.h | 3 + src/provisioning.c | 453 +++++++++++++++++++++++++++++++++++++ src/provisioning.h | 35 +++ 8 files changed, 595 insertions(+), 239 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/provisioning.c create mode 100644 src/provisioning.h diff --git a/.gitignore b/.gitignore index 50f8f64..57ace6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/ sdkconfig +sdkconfig.old dev_keys.txt ttn-esp32 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..248ebc7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "files.associations": { + "provisioning.h": "c", + "config.h": "c", + "sdkconfig.h": "c", + "oslmic.h": "c" + } +} \ No newline at end of file diff --git a/Kconfig b/Kconfig index c6e7035..f1ef14c 100644 --- a/Kconfig +++ b/Kconfig @@ -68,4 +68,75 @@ config TTN_BG_TASK_PRIO the LoRaWAN radio chip. It needs a high priority as the timing is crucial. Higher numbers indicate higher priority. + +choice TTN_PROVISION_UART + prompt "UART for provisioning" + default TTN_PROVISION_UART_DEFAULT + help + Select whether to use UART for listening for provisioning commands. + + - Default is to use UART0 on pins GPIO1(TX) and GPIO3(RX). + - If "Custom" is selected, UART0 or UART1 can be chosen, + and any pins can be selected. + - If "None" is selected, the feature is not available. + +config TTN_PROVISION_UART_DEFAULT + bool "Default: UART0, TX=GPIO1, RX=GPIO3, 115,200 baud" +config TTN_PROVISION_UART_CUSTOM + bool "Custom" +config TTN_PROVISION_UART_NONE + bool "None" +endchoice + +choice TTN_PROVISION_UART_INIT + prompt "Initialize UART" + default TTN_PROVISION_UART_INIT_NO + depends on !TTN_PROVISION_UART_NONE + help + Select whether to initialize the UART, i.e. set the baud rate, the RX and TX + pins. If the UART is shared with other features (e.g. the console), it + should not be initialized. + +config TTN_PROVISION_UART_INIT_NO + bool "No" +config TTN_PROVISION_UART_INIT_YES + bool "Yes" +endchoice + +choice TTN_PROVISION_UART_NUM + prompt "UART peripheral for provisioning (0-1)" + depends on TTN_PROVISION_UART_CUSTOM + default TTN_PROVISION_UART_CUSTOM_NUM_0 + +config TTN_PROVISION_UART_CUSTOM_NUM_0 + bool "UART0" +config TTN_PROVISION_UART_CUSTOM_NUM_1 + bool "UART1" +endchoice + +config TTN_PROVISION_UART_NUM + int + default 0 if TTN_PROVISION_UART_DEFAULT || TTN_PROVISION_UART_NONE + default 0 if TTN_PROVISION_UART_CUSTOM_NUM_0 + default 1 if TTN_PROVISION_UART_CUSTOM_NUM_1 + +config TTN_PROVISION_UART_TX_GPIO + int "Provisioning UART TX on GPIO#" + depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + range 0 33 + default 19 + +config TTN_PROVISION_UART_RX_GPIO + int "Provisioning UART RX on GPIO#" + depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + range 0 39 + default 21 + +config TTN_PROVISION_UART_BAUDRATE + int "Provisioning UART baud rate" + depends on TTN_PROVISION_UART_CUSTOM && TTN_PROVISION_UART_INIT_YES + default 115200 + range 1200 4000000 + + endmenu diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index 2d4f906..99c040e 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -104,6 +104,13 @@ public: */ bool provision(const char *devEui, const char *appEui, const char *appKey); + /** + * @brief Start task that listens on configured UART for provisioning commands. + * + * Run 'make menuconfig' to configure it. + */ + void startProvisioningTask(); + /** * @brief Activate the device via OTAA. * @@ -176,12 +183,8 @@ public: private: TTNMessageCallback messageCallback; - bool haveKeys; bool joinCore(); - bool decodeKeys(const char *devEui, const char *appEui, const char *appKey); - bool saveKeys(); - bool restoreKeys(bool silent); }; #endif diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index 0ca6d79..21b9a7f 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -12,13 +12,13 @@ #include "freertos/FreeRTOS.h" #include "esp_event.h" -#include "nvs_flash.h" -#include "TheThingsNetwork.h" #include "esp_log.h" -#include "oslmic.h" +#include "TheThingsNetwork.h" #include "hal.h" #include "hal_esp32.h" #include "lmic.h" +#include "provisioning.h" + enum ClientAction { @@ -28,29 +28,14 @@ enum ClientAction }; static const char *TAG = "ttn"; -static const char *NVS_FLASH_PARTITION = "ttn"; -static const char *NVS_FLASH_KEY_DEV_EUI = "devEui"; -static const char *NVS_FLASH_KEY_APP_EUI = "appEui"; -static const char *NVS_FLASH_KEY_APP_KEY = "appKey"; static TheThingsNetwork* ttnInstance; -static uint8_t devEui[8]; -static uint8_t appEui[8]; -static uint8_t appKey[16]; static QueueHandle_t resultQueue; static ClientAction clientAction = eActionUnrelated; -static bool readNvsValue(nvs_handle handle, const char* key, uint8_t* data, size_t expectedLength, bool silent); -static bool writeNvsValue(nvs_handle handle, const char* key, const uint8_t* data, size_t len); -static bool hexStringToBin(const char *hex, uint8_t *buf, int len); -static int hexTupleToByte(const char *hex); -static int hexDigitToVal(char ch); -static void swapByteOrder(uint8_t* buf, int len); - - TheThingsNetwork::TheThingsNetwork() - : messageCallback(NULL), haveKeys(false) + : messageCallback(NULL) { ASSERT(ttnInstance == NULL); ttnInstance = this; @@ -89,45 +74,23 @@ void TheThingsNetwork::reset() bool TheThingsNetwork::provision(const char *devEui, const char *appEui, const char *appKey) { - if (!decodeKeys(devEui, appEui, appKey)) + if (!provisioning_decode_keys(devEui, appEui, appKey)) return false; - return saveKeys(); + return provisioning_save_keys(); } -bool TheThingsNetwork::decodeKeys(const char *devEui, const char *appEui, const char *appKey) +void TheThingsNetwork::startProvisioningTask() { - haveKeys = false; - - if (strlen(devEui) != 16 || !hexStringToBin(devEui, ::devEui, 8)) - { - ESP_LOGW(TAG, "Invalid device EUI: %s", devEui); - return false; - } - - swapByteOrder(::devEui, 8); - - if (strlen(appEui) != 16 || !hexStringToBin(appEui, ::appEui, 8)) - { - ESP_LOGW(TAG, "Invalid application EUI: %s", appEui); - return false; - } - - swapByteOrder(::appEui, 8); - - if (strlen(appKey) != 32 || !hexStringToBin(appKey, ::appKey, 16)) - { - ESP_LOGW(TAG, "Invalid application key: %s", appEui); - return false; - } - - haveKeys = true; - return true; +#if !defined(CONFIG_TTN_PROVISION_UART_NONE) + provisioning_start_task(); +#endif } + bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char *appKey) { - if (!decodeKeys(devEui, appEui, appKey)) + if (!provisioning_decode_keys(devEui, appEui, appKey)) return false; return joinCore(); @@ -135,9 +98,9 @@ bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char * bool TheThingsNetwork::join() { - if (!haveKeys) + if (!provisioning_have_keys()) { - if (!restoreKeys(false)) + if (!provisioning_restore_keys(false)) return false; } @@ -146,7 +109,7 @@ bool TheThingsNetwork::join() bool TheThingsNetwork::joinCore() { - if (!haveKeys) + if (!provisioning_have_keys()) { ESP_LOGW(TAG, "Device EUI, App EUI and/or App key have not been provided"); return false; @@ -205,118 +168,13 @@ void TheThingsNetwork::onMessage(TTNMessageCallback callback) messageCallback = callback; } -bool TheThingsNetwork::saveKeys() -{ - bool result = false; - - nvs_handle handle = 0; - esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READWRITE, &handle); - if (res == ESP_ERR_NVS_NOT_INITIALIZED) - { - ESP_LOGW(TAG, "NVS storage is not initialized. Call 'nvs_flash_init()' first."); - goto done; - } - ESP_ERROR_CHECK(res); - if (res != ESP_OK) - goto done; - - if (!writeNvsValue(handle, NVS_FLASH_KEY_DEV_EUI, ::devEui, sizeof(::devEui))) - goto done; - - if (!writeNvsValue(handle, NVS_FLASH_KEY_APP_EUI, ::appEui, sizeof(::appEui))) - goto done; - - if (!writeNvsValue(handle, NVS_FLASH_KEY_APP_KEY, ::appKey, sizeof(::appKey))) - goto done; - - res = nvs_commit(handle); - ESP_ERROR_CHECK(res); - - result = true; - ESP_LOGI(TAG, "Dev and app EUI and app key saved in NVS storage"); - -done: - nvs_close(handle); - return result; -} - -bool TheThingsNetwork::restoreKeys(bool silent) -{ - haveKeys = false; - - nvs_handle handle = 0; - esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READONLY, &handle); - if (res == ESP_ERR_NVS_NOT_FOUND) - return false; // partition does not exist yet - if (res == ESP_ERR_NVS_NOT_INITIALIZED) - { - ESP_LOGW(TAG, "NVS storage is not initialized. Call 'nvs_flash_init()' first."); - goto done; - } - ESP_ERROR_CHECK(res); - if (res != ESP_OK) - goto done; - - if (!readNvsValue(handle, NVS_FLASH_KEY_DEV_EUI, ::devEui, sizeof(::devEui), silent)) - goto done; - - if (!readNvsValue(handle, NVS_FLASH_KEY_APP_EUI, ::appEui, sizeof(::appEui), silent)) - goto done; - - if (!readNvsValue(handle, NVS_FLASH_KEY_APP_KEY, ::appKey, sizeof(::appKey), silent)) - goto done; - - haveKeys = true; - ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); - -done: - nvs_close(handle); - return haveKeys; -} bool TheThingsNetwork::isProvisioned() { - if (haveKeys) + if (provisioning_have_keys()) return true; - return restoreKeys(true); -} - -bool readNvsValue(nvs_handle handle, const char* key, uint8_t* data, size_t expectedLength, bool silent) -{ - size_t size = expectedLength; - esp_err_t res = nvs_get_blob(handle, key, data, &size); - if (res == ESP_OK && size == expectedLength) - return true; - - if (res == ESP_OK && size != expectedLength) - { - if (!silent) - ESP_LOGW(TAG, "Invalid size of NVS data for %s", key); - return false; - } - - if (res == ESP_ERR_NVS_NOT_FOUND) - { - if (!silent) - ESP_LOGW(TAG, "No NVS data found for %s", key); - return false; - } - - ESP_ERROR_CHECK(res); - return false; -} - -bool writeNvsValue(nvs_handle handle, const char* key, const uint8_t* data, size_t len) -{ - uint8_t buf[16]; - if (readNvsValue(handle, key, buf, len, true) && memcmp(buf, data, len) == 0) - return true; // unchanged - - esp_err_t res = nvs_set_blob(handle, key, data, len); - ESP_ERROR_CHECK(res); - - return res == ESP_OK; + return provisioning_restore_keys(true); } @@ -333,29 +191,6 @@ static const char *eventNames[] = { }; #endif -// This EUI must be in little-endian format, so least-significant-byte first. -// When copying an EUI from ttnctl output, this means to reverse the bytes. -// For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70. -// The order is swapped in TheThingsNetwork::provision(). -void os_getArtEui (u1_t* buf) -{ - memcpy(buf, appEui, 8); -} - -// This should also be in little endian format, see above. -void os_getDevEui (u1_t* buf) -{ - memcpy(buf, devEui, 8); -} - -// This key should be in big endian format (or, since it is not really a number -// but a block of memory, endianness does not really apply). In practice, a key -// taken from ttnctl can be copied as-is. -void os_getDevKey (u1_t* buf) -{ - memcpy(buf, appKey, 16); -} - void onEvent (ev_t ev) { #if CONFIG_LOG_DEFAULT_LEVEL >= 3 ESP_LOGI(TAG, "event %s", eventNames[ev]); @@ -385,56 +220,3 @@ void onEvent (ev_t ev) { clientAction = eActionUnrelated; xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS); } - - -// --- Helper functions --- - -bool hexStringToBin(const char *hex, uint8_t *buf, int len) -{ - const char* ptr = hex; - for (int i = 0; i < len; i++) - { - int val = hexTupleToByte(ptr); - if (val < 0) - return false; - buf[i] = val; - ptr += 2; - } - return true; -} - -int hexTupleToByte(const char *hex) -{ - int nibble1 = hexDigitToVal(hex[0]); - if (nibble1 < 0) - return -1; - int nibble2 = hexDigitToVal(hex[1]); - if (nibble2 < 0) - return -1; - return (nibble1 << 4) | nibble2; -} - -int hexDigitToVal(char ch) -{ - if (ch >= '0' && ch <= '9') - return ch - '0'; - if (ch >= 'A' && ch <= 'F') - return ch + 10 - 'A'; - if (ch >= 'a' && ch <= 'f') - return ch + 10 - 'a'; - return -1; -} - -void swapByteOrder(uint8_t* buf, int len) -{ - uint8_t* p1 = buf; - uint8_t* p2 = buf + len - 1; - while (p1 < p2) - { - uint8_t t = *p1; - *p1 = *p2; - *p2 = t; - p1++; - p2--; - } -} \ No newline at end of file diff --git a/src/hal.h b/src/hal.h index 2acd42b..a1d34c6 100755 --- a/src/hal.h +++ b/src/hal.h @@ -28,6 +28,9 @@ #ifndef _hal_hpp_ #define _hal_hpp_ +#include "oslmic.h" + + #ifdef __cplusplus extern "C" { #endif diff --git a/src/provisioning.c b/src/provisioning.c new file mode 100644 index 0000000..e0879e2 --- /dev/null +++ b/src/provisioning.c @@ -0,0 +1,453 @@ +/******************************************************************************* + * + * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x + * + * Copyright (c) 2018 Manuel Bleichenbacher + * + * Licensed under MIT License + * https://opensource.org/licenses/MIT + * + * Task listening on a UART port for provisioning commands. + *******************************************************************************/ + +#include "freertos/FreeRTOS.h" +#include "driver/uart.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "provisioning.h" + +#define UART_NUM CONFIG_TTN_PROVISION_UART_NUM +#define MAX_LINE_LENGTH 128 + +static const char *TAG = "ttn_prov"; +static const char *NVS_FLASH_PARTITION = "ttn"; +static const char *NVS_FLASH_KEY_DEV_EUI = "devEui"; +static const char *NVS_FLASH_KEY_APP_EUI = "appEui"; +static const char *NVS_FLASH_KEY_APP_KEY = "appKey"; + +static void provisioning_task(void* pvParameter); +static void provisioning_add_line_data(int numBytes); +static void provisioning_detect_line_end(int start_at); +static void provisioning_process_line(); +static bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent); +static bool write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, size_t len); +static bool hex_str_to_bin(const char *hex, uint8_t *buf, int len); +static int hex_tuple_to_byte(const char *hex); +static int hex_digit_to_val(char ch); +static void bin_to_hex_str(const uint8_t* buf, int len, char* hex); +static char val_to_hex_digit(int val); +static void swap_bytes(uint8_t* buf, int len); + + +static QueueHandle_t uart_queue = NULL; +static char* line_buf; +static int line_length; +static uint8_t last_line_end_char = 0; +static uint8_t global_dev_eui[8]; +static uint8_t global_app_eui[8]; +static uint8_t global_app_key[16]; +static bool have_keys = false; + + +#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) +static void provisioning_init_uart(); +#endif + + +// --- LMIC callbacks + +// This EUI must be in little-endian format, so least-significant-byte first. +// When copying an EUI from ttnctl output, this means to reverse the bytes. +// For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70. +// The order is swapped in provisioning_decode_keys(). +void os_getArtEui (u1_t* buf) +{ + memcpy(buf, global_app_eui, 8); +} + +// This should also be in little endian format, see above. +void os_getDevEui (u1_t* buf) +{ + memcpy(buf, global_dev_eui, 8); +} + +// This key should be in big endian format (or, since it is not really a number +// but a block of memory, endianness does not really apply). In practice, a key +// taken from ttnctl can be copied as-is. +void os_getDevKey (u1_t* buf) +{ + memcpy(buf, global_app_key, 16); +} + + +// --- Provisioning task + +void provisioning_start_task() +{ +#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) + provisioning_init_uart(); +#endif + + esp_err_t err = uart_driver_install(UART_NUM, 2048, 2048, 20, &uart_queue, 0); + ESP_ERROR_CHECK(err); + + xTaskCreate(provisioning_task, "provisioning", 2048, NULL, 1, NULL); +} + +void provisioning_task(void* pvParameter) +{ + line_buf = (char*)malloc(MAX_LINE_LENGTH + 1); + line_length = 0; + + uart_event_t event; + + ESP_LOGI(TAG, "Provisioning task started"); + + while (true) + { + if (!xQueueReceive(uart_queue, &event, portMAX_DELAY)) + continue; + + switch (event.type) + { + case UART_DATA: + provisioning_add_line_data(event.size); + break; + + case UART_FIFO_OVF: + case UART_BUFFER_FULL: + uart_flush_input(UART_NUM); + xQueueReset(uart_queue); + break; + + default: + break; + } + } +} + +void provisioning_add_line_data(int numBytes) +{ + int n; +top: + n = numBytes; + if (line_length + n > MAX_LINE_LENGTH) + n = MAX_LINE_LENGTH - line_length; + + uart_read_bytes(UART_NUM, (uint8_t*)line_buf + line_length, n, portMAX_DELAY); + int start_at = line_length; + line_length += n; + + provisioning_detect_line_end(start_at); + + if (n < numBytes) + { + numBytes -= n; + goto top; + } +} + +void provisioning_detect_line_end(int start_at) +{ +top: + for (int p = start_at; p < line_length; p++) + { + char ch = line_buf[p]; + if (ch == 0x0d || ch == 0x0a) + { + if (p > 0) + uart_write_bytes(UART_NUM, line_buf + start_at, line_length - start_at - 1); + if (p > 0 || ch == 0x0d || last_line_end_char == 0x0a) + uart_write_bytes(UART_NUM, "\r\n", 2); + + line_buf[p] = 0; + last_line_end_char = ch; + + if (p > 0) + provisioning_process_line(); + + memcpy(line_buf, line_buf + p + 1, line_length - p - 1); + line_length -= p + 1; + start_at = 0; + goto top; + } + } + + if (line_length > 0) + uart_write_bytes(UART_NUM, line_buf + start_at, line_length - start_at); + + if (line_length == MAX_LINE_LENGTH) + line_length = 0; // Line too long; flush it +} + +void provisioning_process_line() +{ + bool is_ok = true; + // Expected format: + // AT+PROV? + // AT+PROV=hex16-hex16-hex32 + if (strcmp(line_buf, "AT+PROV?") == 0) + { + uint8_t binbuf[8]; + char hexbuf[16]; + + memcpy(binbuf, global_dev_eui, 8); + swap_bytes(binbuf, 8); + bin_to_hex_str(binbuf, 8, hexbuf); + uart_write_bytes(UART_NUM, hexbuf, 16); + uart_write_bytes(UART_NUM, "-", 1); + + memcpy(binbuf, global_app_eui, 8); + swap_bytes(binbuf, 8); + bin_to_hex_str(binbuf, 8, hexbuf); + uart_write_bytes(UART_NUM, hexbuf, 16); + + uart_write_bytes(UART_NUM, "-00000000000000000000000000000000\r\n", 35); + } + else if (strncmp(line_buf, "AT+PROV=", 8) == 0) + { + if (strlen(line_buf) == 74 && line_buf[24] == '-' && line_buf[41] == '-') + { + line_buf[24] = 0; + line_buf[41] = 0; + is_ok = provisioning_decode_keys(line_buf + 8, line_buf + 25, line_buf + 42); + } + else + { + is_ok = false; + } + } + else + { + is_ok = false; + } + + uart_write_bytes(UART_NUM, is_ok ? "OK\r\n" : "ERROR\r\n", is_ok ? 4 : 7); +} + +#if defined(CONFIG_TTN_PROVISION_UART_INIT_YES) + +void provisioning_init_uart() +{ + +} + +#endif + + +// --- Key handling + +bool provisioning_have_keys() +{ + return have_keys; +} + +bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key) +{ + have_keys = false; + + if (strlen(dev_eui) != 16 || !hex_str_to_bin(dev_eui, global_dev_eui, 8)) + { + ESP_LOGW(TAG, "Invalid device EUI: %s", dev_eui); + return false; + } + + swap_bytes(global_dev_eui, 8); + + if (strlen(app_eui) != 16 || !hex_str_to_bin(app_eui, global_app_eui, 8)) + { + ESP_LOGW(TAG, "Invalid application EUI: %s", app_eui); + return false; + } + + swap_bytes(global_app_eui, 8); + + if (strlen(app_key) != 32 || !hex_str_to_bin(app_key, global_app_key, 16)) + { + ESP_LOGW(TAG, "Invalid application key: %s", app_key); + return false; + } + + have_keys = true; + return true; +} + + +// --- Non-volatile storage + +bool provisioning_save_keys() +{ + bool result = false; + + nvs_handle handle = 0; + esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READWRITE, &handle); + if (res == ESP_ERR_NVS_NOT_INITIALIZED) + { + ESP_LOGW(TAG, "NVS storage is not initialized. Call 'nvs_flash_init()' first."); + goto done; + } + ESP_ERROR_CHECK(res); + if (res != ESP_OK) + goto done; + + if (!write_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui))) + goto done; + + if (!write_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui))) + goto done; + + if (!write_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key))) + goto done; + + res = nvs_commit(handle); + ESP_ERROR_CHECK(res); + + result = true; + ESP_LOGI(TAG, "Dev and app EUI and app key saved in NVS storage"); + +done: + nvs_close(handle); + return result; +} + +bool provisioning_restore_keys(bool silent) +{ + have_keys = false; + + nvs_handle handle = 0; + esp_err_t res = nvs_open(NVS_FLASH_PARTITION, NVS_READONLY, &handle); + if (res == ESP_ERR_NVS_NOT_FOUND) + return false; // partition does not exist yet + if (res == ESP_ERR_NVS_NOT_INITIALIZED) + { + ESP_LOGW(TAG, "NVS storage is not initialized. Call 'nvs_flash_init()' first."); + goto done; + } + ESP_ERROR_CHECK(res); + if (res != ESP_OK) + goto done; + + if (!read_nvs_value(handle, NVS_FLASH_KEY_DEV_EUI, global_dev_eui, sizeof(global_dev_eui), silent)) + goto done; + + if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_EUI, global_app_eui, sizeof(global_app_eui), silent)) + goto done; + + if (!read_nvs_value(handle, NVS_FLASH_KEY_APP_KEY, global_app_key, sizeof(global_app_key), silent)) + goto done; + + have_keys = true; + ESP_LOGI(TAG, "Dev and app EUI and app key have been restored from NVS storage"); + +done: + nvs_close(handle); + return have_keys; +} + +bool read_nvs_value(nvs_handle handle, const char* key, uint8_t* data, size_t expected_length, bool silent) +{ + size_t size = expected_length; + esp_err_t res = nvs_get_blob(handle, key, data, &size); + if (res == ESP_OK && size == expected_length) + return true; + + if (res == ESP_OK && size != expected_length) + { + if (!silent) + ESP_LOGW(TAG, "Invalid size of NVS data for %s", key); + return false; + } + + if (res == ESP_ERR_NVS_NOT_FOUND) + { + if (!silent) + ESP_LOGW(TAG, "No NVS data found for %s", key); + return false; + } + + ESP_ERROR_CHECK(res); + return false; +} + +bool write_nvs_value(nvs_handle handle, const char* key, const uint8_t* data, size_t len) +{ + uint8_t buf[16]; + if (read_nvs_value(handle, key, buf, len, true) && memcmp(buf, data, len) == 0) + return true; // unchanged + + esp_err_t res = nvs_set_blob(handle, key, data, len); + ESP_ERROR_CHECK(res); + + return res == ESP_OK; +} + + +// --- Helper functions --- + +bool hex_str_to_bin(const char *hex, uint8_t *buf, int len) +{ + const char* ptr = hex; + for (int i = 0; i < len; i++) + { + int val = hex_tuple_to_byte(ptr); + if (val < 0) + return false; + buf[i] = val; + ptr += 2; + } + return true; +} + +int hex_tuple_to_byte(const char *hex) +{ + int nibble1 = hex_digit_to_val(hex[0]); + if (nibble1 < 0) + return -1; + int nibble2 = hex_digit_to_val(hex[1]); + if (nibble2 < 0) + return -1; + return (nibble1 << 4) | nibble2; +} + +int hex_digit_to_val(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch + 10 - 'A'; + if (ch >= 'a' && ch <= 'f') + return ch + 10 - 'a'; + return -1; +} + +void bin_to_hex_str(const uint8_t* buf, int len, char* hex) +{ + for (int i = 0; i < len; i++) + { + uint8_t b = buf[i]; + *hex = val_to_hex_digit((b & 0xf0) >> 4); + hex++; + *hex = val_to_hex_digit(b & 0x0f); + hex++; + } +} + +char val_to_hex_digit(int val) +{ + return "0123456789ABCDEF"[val]; +} + +void swap_bytes(uint8_t* buf, int len) +{ + uint8_t* p1 = buf; + uint8_t* p2 = buf + len - 1; + while (p1 < p2) + { + uint8_t t = *p1; + *p1 = *p2; + *p2 = t; + p1++; + p2--; + } +} diff --git a/src/provisioning.h b/src/provisioning.h new file mode 100644 index 0000000..3d1900f --- /dev/null +++ b/src/provisioning.h @@ -0,0 +1,35 @@ +/******************************************************************************* + * + * ttn-esp32 - The Things Network device library for ESP-IDF / SX127x + * + * Copyright (c) 2018 Manuel Bleichenbacher + * + * Licensed under MIT License + * https://opensource.org/licenses/MIT + * + * Task listening on a UART port for provisioning commands. + *******************************************************************************/ + +#ifndef _provision_task_h_ +#define _provision_task_h_ + +#include "oslmic.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +void provisioning_start_task(); +bool provisioning_have_keys(); +bool provisioning_decode_keys(const char *dev_eui, const char *app_eui, const char *app_key); +bool provisioning_save_keys(); +bool provisioning_restore_keys(bool silent); + + +#ifdef __cplusplus +} +#endif + +#endif