
Это вторая статья о тестовой системе аутентификации удалённого контроллера в беспроводной LoRa сети. В первой части мы подробно рассмотрели устройство и работу базового контроллера (т. н. LoRa «базы»), в этой статье мы рассмотрим работу и устройство ответной части, т. н. «датчика».
Эта статья предполагает, что перед её чтением вы ознакомились с первой частью, где объясняется работа базы и основные принципы построения тестовой LoRa крипто-системы.
Постановка задачи
Напомним, что нам нужно создать тестовую систему, состоящую из «базы» и «датчика», связанных по беспроводному LoRa каналу. Задача состоит в том, что базе нужно связаться с датчиком и удостовериться, что это именно «легитимный» датчик нашей экосистемы, а не клонированный хакером (чужой) датчик.
Решается эта задача при помощи микросхем ATSHA204A, которые установлены на платах базы и датчика и соответствующим образом запрограммированы.
Оборудование
В качестве контроллеров для эксперимента (и для базы и для датчика) используются беспроводные контроллеры uniSensors LoRa, которые имеют на борту все необходимые компоненты.
- Микроконтроллер ATmega328
- Беспроводной LoRa модуль
- Микросхему ATSHA204A
Примечание. Для работы с uniSensors LoRa и компиляции скетчей в Arduino IDE в настройках нужно выбирать контроллер Arduino Pro Mini, Atmega328 (3.3V, 8MHz).
Алгоритм работы
Алгоритм работы, обеспечивающий аутентификацию датчика, следующий: на базе микросхемой ATSHA204A генерируется (RNG) случайное число, затем этой же микросхемой при помощи команды MAC (на основе случайного числа и секретного ключа в Data зоне EEPROM памяти ATSHA204A) вычисляется SHA-256 хеш (дайджест). Этот дайджест запоминается для последующего сравнения с ответом датчика.
Затем ранее сгенерированное случайное число по беспроводному LoRa каналу отсылается датчику, который на его основе (при помощи идентично запрограммированной микросхемы ATSHA204A) также вычисляет SHA-256 хеш и отсылает его обратно базе для проверки и сравнения с заранее вычисленным на базе хешем.
Вот графическое представление работы алгоритма аутентификации:

Более подробно об этом алгоритме и реализации его работы микросхемой ATSHA204A вы можете прочитать в предыдущих статьях этого цикла.
Датчик
В этой статье мы подробно рассмотрим работу датчика («ответной части») как с криптографическими алгоритмами, так и работу с беспроводной системой LoRa. Работу базы мы подробно разобрали в предыдущей статье.
Как следует из условий задачи, датчик должен получить от базы случайное число, вычислить с его участием хеш и отослать этот хеш (дайджест) обратно базе. Это основной функционал датчика, теперь подробнее разберём создание и работу скетча.
Код криптоалгоритма датчика
Ниже приведён код криптоалгоритма датчика из одной из наших предыдущих статей (там же вы можете ознакомиться с подробным описанием его работы). Здесь наша задача будет состоять в интеграции этого кода со скетчем беспроводной LoRa связи.
/*
ATSHA204A Project (Sensor)
*/
#include <sha204_library.h>
const int sha204Pin = A3;
byte rnd[MAC_CHALLENGE_SIZE] = {
0x23, 0x47, 0x3B, 0x1D, 0x89, 0xEA, 0x07, 0x80,
0x07, 0x5A, 0xEE, 0xD9, 0xEC, 0x8D, 0xF5, 0x68,
0x88, 0xAC, 0x3B, 0x36, 0x50, 0xF4, 0x03, 0xA4,
0xDF, 0xB3, 0x38, 0xA7, 0x16, 0xBA, 0x4A, 0xF5
};
byte challenge[MAC_CHALLENGE_SIZE];
byte hash[MAC_CHALLENGE_SIZE];
byte retCode = 0;
atsha204Class sha204(sha204Pin);
void setup() {
Serial.begin(115200);
Serial.println("ATSHA204 Project (Sensor) start...");
getRandom();
Serial.println("Random:");
printArray(challenge, 32);
Serial.println();
macChallenge();
Serial.println("Hash:");
printArray(hash, 32);
Serial.println();
}
// Print
void printArray(byte arr[], byte len) {
for (byte i = 0; i < len; i++) {
if (arr[i] < 16) {Serial.print('0');}
Serial.print(arr[i], HEX); Serial.print(' ');
}
}
// Random
void getRandom() {
for (byte i = 0; i < 32; i++) {
challenge[i] = rnd[i];
}
}
// MAC
void macChallenge() {
uint8_t command[MAC_COUNT_LONG];
uint8_t response[MAC_RSP_SIZE];
retCode = sha204.sha204m_execute(SHA204_MAC,
0, 0,
MAC_CHALLENGE_SIZE, (uint8_t *) challenge,
0, NULL,
0, NULL,
sizeof(command), &command[0],
sizeof(response), &response[0]);
for (byte i = 0; i < 32; i++) {
hash[i] = response[i];
}
}
void loop() {
}
Подсистема LoRa датчика
Работа с беспроводной системой LoRa является достаточно сложной и специфичной темой и подробное рассмотрение её выходит за рамки этой статьи, поэтому здесь мы только приведём готовый LoRa скетч датчика (без крипто-функций), а затем создадим финальный код скетча, который будет сочетать в себе и крипто-функционал (датчика) и возможности беспроводной LoRa связи.
Для работы этого скетча и вообще для работы с LoRa подсистемой вам понадобится библиотека Arduino LoRa.
/*
LoRa Sensor
*/
#include <SPI.h>
#include <LoRa.h>
#define LORA_POWER_PIN A1
#define LORA_SS_PIN 7
#define LORA_RESET_PIN 6
#define LORA_DIO0_PIN 5
#define PACKET_MAX_BYTES 32
byte buffRx[PACKET_MAX_BYTES];
void setup() {
Serial.begin(115200);
Serial.println("LoRa Sensor");
pinMode(LORA_POWER_PIN, OUTPUT);
digitalWrite(LORA_POWER_PIN, LOW);
delay(500);
LoRa.setPins(LORA_SS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);
if (!LoRa.begin(868E6)) {
Serial.println("LoRa init failed");
while(true);
}
}
// Print
void printArray(byte arr[], byte len) {
for (byte i = 0; i < len; i++) {
if (arr[i] < 16) {Serial.print('0');}
Serial.print(arr[i], HEX); Serial.print(' ');
}
}
// Send
void sendPacket() {
LoRa.beginPacket();
LoRa.write(buffRx, PACKET_MAX_BYTES);
LoRa.endPacket();
Serial.print("Send back: ");
printArray(buffRx, PACKET_MAX_BYTES);
Serial.println();
Serial.println();
}
// Receive
void onReceive(int packetSize) {
if (packetSize == 0) {return;}
if (packetSize != PACKET_MAX_BYTES) {
Serial.println("Bad packet");
return;
}
int i = -1;
while (LoRa.available()) {
i++;
if (i > 31) {break;}
buffRx[i] = LoRa.read();
}
Serial.print("Receive: ");
printArray(buffRx, PACKET_MAX_BYTES);
Serial.println();
delay(200);
sendPacket();
}
void loop() {
onReceive(LoRa.parsePacket());
}
Этот скетч выполняет всего одну простую функцию: принимает от базы тестовый 32-байтовый массив buffRx и отсылает его обратно на базу. Получается «беспроводное зеркало», которое отражает обратно принимаемые данные.
Вначале мы подключаем все необходимые библиотеки для работы с LoRa частью.
#include <SPI.h> #include <LoRa.h>
Затем определяем пин включения питания радиомодуля на контроллерах uniSensors LoRa.
#define LORA_POWER_PIN A1
Также определяем пины для управления LoRa модулем.
#define LORA_SS_PIN 7 #define LORA_RESET_PIN 6 #define LORA_DIO0_PIN 5
Задаём тестовый 32-байтовый массив для получения и отсылки данных.
#define PACKET_MAX_BYTES 32 byte buffRx[PACKET_MAX_BYTES];
Затем, в функции setup(), подаём питание на радиомодуль uniSensors LoRa.
pinMode(LORA_POWER_PIN, OUTPUT); digitalWrite(LORA_POWER_PIN, LOW); delay(500);
Задаём пины управления LoRa модулем.
LoRa.setPins(LORA_SS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);
И включаем LoRa модуль на частоте 868 МГц.
if (!LoRa.begin(868E6)) {
Serial.println("LoRa init failed");
while(true);
}
Далее в цикле loop() ждём данные от базы и принимаем их.
void loop() {
onReceive(LoRa.parsePacket());
}
В функции onReceive() принимаем 32-байтовый массив от базы и выводим его на печать.
void onReceive(int packetSize) {
if (packetSize == 0) {return;}
if (packetSize != PACKET_MAX_BYTES) {
Serial.println("Bad packet");
return;
}
int i = -1;
while (LoRa.available()) {
i++;
if (i > 31) {break;}
buffRx[i] = LoRa.read();
}
Serial.print("Receive: ");
printArray(buffRx, PACKET_MAX_BYTES);
Serial.println();
Затем ждём 200 миллисекунд и отправляем его обратно.
delay(200); sendPacket();
Функция sendPacket() отправляет массив buffRx и выводит его на печать.
void sendPacket() {
LoRa.beginPacket();
LoRa.write(buffRx, PACKET_MAX_BYTES);
LoRa.endPacket();
Serial.print("Send back: ");
printArray(buffRx, PACKET_MAX_BYTES);
Serial.println();
Serial.println();
}
Скетч рассчитан на работу с «базовой» частью (скетчем базы), который мы описали в предыдущей статье.
Далее мы создадим из этих двух (криптографического ATSHA204A и беспроводного LoRa) объединённый финальный скетч датчика и подробно разберём его работу.
Скетч LoRa ATSHA204A Sensor
Ниже представлен полный код скетча LoRa ATSHA204A Sensor, объединяющий в себе криптографическую, беспроводную и функциональную части.
/*
LoRa ATSHA204A Sensor
*/
#include <SPI.h>
#include <LoRa.h>
#include <sha204_library.h>
// LoRa
#define LORA_POWER_PIN A1
#define LORA_SS_PIN 7
#define LORA_RESET_PIN 6
#define LORA_DIO0_PIN 5
#define PACKET_MAX_BYTES 32
byte buffRx[PACKET_MAX_BYTES];
// ATSHA204A
const int sha204Pin = A3;
byte challenge[MAC_CHALLENGE_SIZE];
byte hash[MAC_CHALLENGE_SIZE];
byte retCode = 0;
atsha204Class sha204(sha204Pin);
void setup() {
Serial.begin(115200);
Serial.println("LoRa ATSHA204A Sensor start...");
pinMode(LORA_POWER_PIN, OUTPUT);
digitalWrite(LORA_POWER_PIN, LOW);
delay(500);
LoRa.setPins(LORA_SS_PIN, LORA_RESET_PIN, LORA_DIO0_PIN);
if (!LoRa.begin(868E6)) {
Serial.println("LoRa init failed");
while(true);
}
}
// Print
void printArray(byte arr[], byte len) {
for (byte i = 0; i < len; i++) {
if (arr[i] < 16) {Serial.print('0');}
Serial.print(arr[i], HEX); Serial.print(' ');
}
}
// MAC
void macChallenge() {
uint8_t command[MAC_COUNT_LONG];
uint8_t response[MAC_RSP_SIZE];
retCode = sha204.sha204m_execute(SHA204_MAC,
0, 0,
MAC_CHALLENGE_SIZE, (uint8_t *) buffRx,
0, NULL,
0, NULL,
sizeof(command), &command[0],
sizeof(response), &response[0]);
for (byte i = 0; i < 32; i++) {
hash[i] = response[i + 1];
}
}
// Send
void sendPacket() {
LoRa.beginPacket();
LoRa.write(hash, PACKET_MAX_BYTES);
LoRa.endPacket();
Serial.print("Send back: ");
printArray(hash, PACKET_MAX_BYTES);
Serial.println();
Serial.println();
}
// Receive
void onReceive(int packetSize) {
if (packetSize == 0) {return;}
if (packetSize != PACKET_MAX_BYTES) {
Serial.println("Bad packet");
return;
}
int i = -1;
while (LoRa.available()) {
i++;
if (i > 31) {break;}
buffRx[i] = LoRa.read();
}
Serial.print("Receive: ");
printArray(buffRx, PACKET_MAX_BYTES);
Serial.println();
macChallenge();
delay(200);
sendPacket();
}
void loop() {
onReceive(LoRa.parsePacket());
}
Теперь разберём работу этого скетча. В финальной версии датчика мы объявляем 32-байтовый массив для приёма случайного числа от базы нашей IoT системы.
#define PACKET_MAX_BYTES 32 byte buffRx[PACKET_MAX_BYTES];
Объявляем 2 массива: для случайного числа (challenge) и вычисленного хеша SHA-256 (hash).
byte challenge[MAC_CHALLENGE_SIZE]; byte hash[MAC_CHALLENGE_SIZE];
Принимаем от базы случайное 32-байтовое число.
void onReceive(int packetSize) {
if (packetSize == 0) {return;}
if (packetSize != PACKET_MAX_BYTES) {
Serial.println("Bad packet");
return;
}
int i = -1;
while (LoRa.available()) {
i++;
if (i > 31) {break;}
buffRx[i] = LoRa.read();
}
Выводим его на печать и вычисляем (при помощи ATSHA204A) 32-байтовый SHA-256 хеш (дайджест).
Serial.print("Receive: ");
printArray(buffRx, PACKET_MAX_BYTES);
Serial.println();
macChallenge();
Затем отсылаем этот хеш на базу (в ответ на присланное базой случайное число) и выводим его на печать.
void sendPacket() {
LoRa.beginPacket();
LoRa.write(hash, PACKET_MAX_BYTES);
LoRa.endPacket();
Serial.print("Send back: ");
printArray(hash, PACKET_MAX_BYTES);
Serial.println();
Serial.println();
}
Вот результат работы финального объединённого скетча LoRa датчика:

Вы видите присланные случайные числа от базы и отправленный на базу вычисленный ответ (SHA-256 хеш).
Примечание: в этом эксперименте использовались микросхемы ATSHA204A (установленные на платах uniSensors) без дополнительной прошивки и блокировки зон памяти, поэтому вместо реальных случайных чисел вы видите тестовые последовательности FF FF 00 00 FF FF 00 00, выдаваемые микросхемой ATSHA204A в этом режиме. Если вы воспользуетесь методикой программирования микросхем ATSHA204A, описанной в статьях этого цикла, то сможете записать любой секретный ключ в нулевую (0) ячейку Data зоны вашей микросхемы и (без каких-либо переделок этого скетча) получить полнофункциональную аутентификацию при помощи ATSHA204A в вашей беспроводной LoRa сети.
Заключение
Таким образом мы создали два скетча базы и датчика, которые при помощи микросхем ATSHA204A осуществляют криптографическую аутентификацию в LoRa сети. В этой системе «хакер» уже не сможет включить в систему свой клонированный датчик и подделать показания вашего «легитимного» контроллера.
Правда тут нужно понимать, что это тестовый (хотя и функциональный) пример, а в реальных IoT проектах вышеописанный алгоритм аутентификации нужно дополнять различными мерами крипто-безопасности (такими как шифрование и т. п.).
Ссылки по теме
ATSHA204 - Библиотека и примеры
ATSHA204A - Чтение зоны конфигурации 1
ATSHA204A - Чтение зоны конфигурации 2
ATSHA204A - Чтение зоны конфигурации 3
ATSHA204A - Запись конфигурации 1
ATSHA204A - Запись конфигурации 2
ATSHA204A - Запись конфигурации 3
ATSHA204A - Запись конфигурации 4
ATSHA204A - Работа в режиме Config Lock
ATSHA204A - Работа с зонами памяти
ATSHA204A - Чтение Data и OTP зон памяти
ATSHA204A - Аутентификация. Базовый блок
ATSHA204A - Криптография и команды
ATSHA204A - nRF24 аутентификация. База
ATSHA204A - nRF24 аутентификация. Датчик
ATSHA204A - LoRa аутентификация. База
ATSHA204A - LoRa аутентификация. Датчик



