Add provisioning task, refactor provisioning

This commit is contained in:
Manuel Bleichenbacher
2018-07-23 09:46:45 +02:00
parent 2dd5b05891
commit b179ce6884
8 changed files with 595 additions and 239 deletions

View File

@ -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--;
}
}